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<

因此,我无意中展示了如何在Rust中创建“不可移动”类型,因为编译器在其整个生命周期中将对象视为借用对象,因此可以防止移动

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;
    }