Rust 为什么使用单元来创建不可移动的对象?
因此,我无意中展示了如何在Rust中创建“不可移动”类型,因为编译器在其整个生命周期中将对象视为借用对象,因此可以防止移动Rust 为什么使用单元来创建不可移动的对象?,rust,Rust,因此,我无意中展示了如何在Rust中创建“不可移动”类型,因为编译器在其整个生命周期中将对象视为借用对象,因此可以防止移动 use std::cell::Cell; use std::marker; struct Unmovable<'a> { lock: Cell<marker::ContravariantLifetime<'a>>, marker: marker::NoCopy } impl<'a> Unmovable<
use std::cell::Cell;
use std::marker;
struct Unmovable<'a> {
lock: Cell<marker::ContravariantLifetime<'a>>,
marker: marker::NoCopy
}
impl<'a> Unmovable<'a> {
fn new() -> Unmovable<'a> {
Unmovable {
lock: Cell::new(marker::ContravariantLifetime),
marker: marker::NoCopy
}
}
fn lock(&'a self) {
self.lock.set(marker::ContravariantLifetime);
}
fn new_in(self_: &'a mut Option<Unmovable<'a>>) {
*self_ = Some(Unmovable::new());
self_.as_ref().unwrap().lock();
}
}
fn main(){
let x = Unmovable::new();
x.lock();
// error: cannot move out of `x` because it is borrowed
// let z = x;
let mut y = None;
Unmovable::new_in(&mut y);
// error: cannot move out of `y` because it is borrowed
// let z = y;
assert_eq!(std::mem::size_of::<Unmovable>(), 0)
}
然后允许“不可移动”对象移动。为什么会这样?一个有趣的问题!这是我对它的理解 下面是另一个不使用
单元格的示例:
#![feature(core)]
use std::marker::InvariantLifetime;
struct Unmovable<'a> { //'
lock: Option<InvariantLifetime<'a>>, //'
}
impl<'a> Unmovable<'a> {
fn lock_it(&'a mut self) { //'
self.lock = Some(InvariantLifetime)
}
}
fn main() {
let mut u = Unmovable { lock: None };
u.lock_it();
let v = u;
}
唯一的问题是,您需要某种方式让结构包含自己的引用,这在构造时是不可能做到的。我的示例使用选项
,它需要&mut self
,链接的示例使用单元格
,它允许内部的可变性,只需&self
这两个示例都使用生存期标记,因为它允许typesystem跟踪生存期,而无需担心特定实例
让我们看看您的构造函数:
fn new() -> Unmovable<'a> { //'
Unmovable {
lock: marker::ContravariantLifetime,
marker: marker::NoCopy
}
}
在这里,编译器知道生存期不会改变。但是,如果我们使其可变:
fn lock(&'a mut self) {
}
砰!又锁上了。这是因为编译器知道内部字段可能会更改。实际上,我们可以将此应用于我们的选项
变体,并移除锁体
真正的答案包括对生命周期的适度复杂的考虑,以及需要整理的代码中一些误导性的方面
对于下面的代码,'a
是任意生存期,'small
是小于'a
的任意生存期(这可以通过约束'a:'small
)来表示,'static
被用作大于'a
的生存期的最常见示例
以下是考虑中应遵循的事实和步骤:
- 通常情况下,寿命是相反的<代码>&'a T
与'a
相反(与T
一样)
单元格
包装,并将反变量生命周期
(实际上相当于仅定义结构不可移动>
更改为不变量生命周期{
锁定:标记::不变量生存期不可移动{
不可移动{
锁定:标记::不变量生存期,
}
}
fn锁(&'a self){}
新南威尔士州(&'a mut option)我的印象是Rust应该能够严格根据函数签名跟踪生命周期和跨调用借用,对吗?因此,如果我们将单元格
保留在锁定
字段周围,该字段的生存期仍然由调用方决定…单元格
授予内部可变性,但在这两种情况下,lock
的函数原型中都不可见,因此这不应该改变借用检查器的结果,对吗?借用检查器是否理解如果结构包含单元格,则可能会发生变异?这是一个很好的问题。如果这里没有任何其他可了解的注释,我会鼓励ge将其作为另一个顶级问题提交。在这一点上,我觉得这有点像一个bug。我还不能清楚地说明发生了什么,但更改借用类型不应改变在尝试移动时是否借用变量的事实。使借用mut
真的不应该e更改了借用的生存期。即使该值是不可变借用的,移动也应该是不可能的。似乎有点可疑…无论如何,感谢您的帮助!@AidanCully即使该值是不可变借用的-但是请注意,如果我们调用&self
方法,那么我们知道借用不可能生存在函数调用上-没有办法保留它!但是,当使用和'a mut self
,以及带有'a
的相应字段时,该方法可能会修改结构以保留生存期。但这仍然无法清除有关单元格的详细信息。@Shepmaster:注意和'aself
;这意味着该方法只有在实际可以借用时才可调用。如果您有一个函数使用&不可移动的,您将无法对其调用lock()
。(顺便说一句,我不同意您对问题的分析,也不同意@Aidan的说法,它看起来像一个bug。)回答了我所有的问题!除了一个似乎无法回答的问题(为什么您可以使用协变生命周期
或反协变生命周期
?),这似乎是另一个可能的编译器错误。谢谢!声明的和'a mut T
的不变性是不正确的:它对于T
是不变性的,对于'a
是逆变的。将和'a mut
强制为和'small mut
。此外,不变性时间
是ContravarianceLifetime
和CovarianceLifetime
的r部分,因此在限制方面,它的行为与这两个部分类似。每种标记类型实际上都表示删除功能,而不是添加功能:ContravarianceLifetime…
只是不协变,Co.
只是不协变,中的也不是协变的或逆变的。
fn new() -> Unmovable<'a> { //'
Unmovable {
lock: marker::ContravariantLifetime,
marker: marker::NoCopy
}
}
fn lock(&'a self) {
}
fn lock(&'a mut self) {
}
use std::marker;
struct Unmovable<'a> {
lock: marker::InvariantLifetime<'a>,
}
impl<'a> Unmovable<'a> {
fn new() -> Unmovable<'a> {
Unmovable {
lock: marker::InvariantLifetime,
}
}
fn lock(&'a self) { }
fn new_in(_: &'a mut Option<Unmovable<'a>>) { }
}
fn main() {
let x = Unmovable::new();
x.lock();
// This is an error, as desired:
let z = x;
let mut y = None;
Unmovable::new_in(&mut y);
// Yay, this is an error too!
let z = y;
}