首页 文章

需要澄清关于`Box`,`Vec`和其他集合的(共)方差的Rust Nomicon部分

提问于
浏览
9

Rust Nomicon有an entire section on variance,我或多或少都能理解,除了关于 Box<T>Vec<T> 的这一小部分是 T 的(共)变体 .

Box和Vec是有趣的案例,因为它们是变体,但你绝对可以存储 Value !这就是Rust变得非常聪明的地方:它们很适合变种,因为你只能通过可变引用存储它们中的值!可变引用使整个类型不变,因此可以防止您将短期类型走私到它们中 .

令我困惑的是以下几行:

它们很适合变种,因为你只能通过可变引用存储它们中的值!

我的第一个问题是,我对可变引用的含义感到有些困惑 . 它是 Box / Vec 的可变引用吗?

如果是这样,那么我只能通过可变参考在其中存储值的事实如何证明它们的(共)方差?我理解(共)方差是什么以及将它用于 Box<T>Vec<T> 等等的好处,但我很难看到只能通过可变引用存储值与(共)方差的理由之间的联系 .

另外,当我们初始化一个 Box 时,这是否与我们只能通过可变引用存储值的声明相矛盾?

最后,在什么情况下借这个_982138?它们是否意味着当您调用修改 BoxVec 的方法时,您隐含地采用 &mut self ?这是提到的可变参考吗?


Update 2nd May 2018

由于我还没有得到这个问题的满意答案,我认为这个nomicon的解释真的令人困惑 . 正如在下面的评论帖中所承诺的那样,我已经打开了an issue in the Rust Nomicon repository . 您可以跟踪那里的任何更新 .

4 回答

  • 1

    我认为该部分可以使用一些工作来使其更清晰 .

    我对可变引用的含义感到有些困惑 . 它是Box / Vec的可变参考吗?

    不 . 这意味着,如果您将值存储在现有的 Box 中,则必须通过对数据的可变引用来执行此操作,例如使用 Box::borrow_mut() .

    本节试图传达的主要思想是,当存在对内容的另一个引用时,您无法修改 Box 的内容 . 这是有保证的,因为 Box 拥有其内容 . 为了更改 Box 的内容,您必须通过采用新的可变引用来实现 .

    这意味着 - 即使你用较短的值覆盖内容 - 也没关系,因为没有其他人可以使用旧的值 . 借款检查员不会允许 .

    这与函数参数不同,因为函数有一个代码块,它实际上可以用它的参数做事 . 在 BoxVec 的情况下,您必须通过可变地借用它们来获取内容,然后才能对它们执行任何操作 .

  • 0

    来自nomicom

    Box和Vec是有趣的案例,因为它们是变体,但你绝对可以存储 Value !这就是Rust变得非常聪明的地方:它们很适合变种,因为你只能通过可变引用存储它们中的值!可变引用使整个类型不变,因此可以防止您将短期类型走私到它们中 .

    考虑使用 Vec 方法添加值:

    pub fn push(&'a mut self, value: T)
    

    self的类型是 &'a mut Vec<T> ,我理解这是nomicom所说的可变引用,所以为 Vec case实例化上述短语的最后一句变为:

    类型 &'a mut Vec<T> 是不变的,因此可以防止您将短期类型走私到 Vec<T> 中 .

    Box的理由相同 .

    换句话来说: VecBox 包含的值总是比它们的容器寿命更长,尽管 VecBox 是变体,因为你只能通过可变引用将值存储在它们中 .

    请考虑以下代码段:

    fn main() {
        let mut v: Vec<&String> = Vec::new();
    
        {
            let mut a_value = "hola".to_string();
    
            //v.push(a_ref);
            Vec::push(&mut v, &mut a_value);
        }
    
        // nomicom is saing that if &mut self Type was variant here we have had
        // a vector containing a reference pointing to freed memory
    
        // but this is not the case and the compiler throws an error
    }
    

    它应该有助于注意 Vec::push(&mut v, &mut a_value)overwrite(&mut forever_str, &mut &*string) 与nomicom示例的相似性 .

  • 2

    我想重点是,虽然你可以将 Box<&'static str> 转换为 Box<&'a str> (因为 Box<T> 是协变的),但你不能将 &mut Box<&'static str> 转换为 &mut Box<&'a str> (因为 &mut T 是不变的) .

  • 0

    自从在Nomicon回购中打开这个问题以来,维护人员已经介绍了一个revision to the section,我觉得这个问题要清楚得多 . 修订已合并 . 我认为修改后回答了我的问题 .

    下面我简要总结一下我所知道的内容 .

    与我的问题相关的部分现在如下(强调我的):

    Box和Vec是有趣的案例,因为它们是协变的,但你绝对可以存储 Value !这就是Rust的类型系统允许它比其他类型更聪明的地方 . 要理解为什么拥有容器的协变性是合理的他们的内容,我们必须考虑突变可能发生的两种方式:按值或按参考 . 如果变量是按值,那么记住额外细节的旧位置将被移出,这意味着它不能再使用该值 . 所以我们根本不需要担心任何人记住危险的细节 . 换句话说,在传递by-value时应用子类型会永远破坏细节 . 例如,这编译并且很好:fn get_box <'a>(str:&'a str) - > Box <&'a str> {
    //字符串文字是&'静态strs,但它对我们来说很好
    //“忘记”这个并让调用者认为字符串不能存活那么久 .
    Box :: new(“你好”)}
    如果变异是引用,那么我们的容器将作为&mut Vec <T>传递 . 但是&mut对它的值是不变的,所以&mut Vec <T>实际上对T不变 . 因此Vec <T>在T上的协变性这一事实在通过引用变异时根本不重要 .

    这里的关键点实际上是 &mut Vec<T>T 上的不变性与 &mut TT 上的不变性之间的平行关系 .

    之前在修订后的nomicon部分解释了为什么一般 &mut T 不能在 T 上进行协变 . &mut T 借用 T ,但它不拥有 T ,这意味着还有其他东西引用 T 并对其生命周期有一定的期望 .

    但是如果允许我们通过 T 传递 &mut T 协变,那么nomicon示例中的 overwrite 函数显示了我们如何从不同的位置(即 overwrite 体内)打破调用者位置 T 的生命周期 .

    从某种意义上说,允许一个类型构造函数的 T 的协方差允许我们'忘记 T 的原始生命周期___忘记 T 的原始生命周期'对于 &T 是可以的,因为我们没有机会通过它修改 T ,但它很危险我们有一个 &mut T 因为我们能够在忘记生命周期细节之后修改 T . 这就是 &mut T 需要在 T 上不变的原因 .

    It seems the point the nomicon is trying to make is: it's OK for Box<T> to be covariant over T because it does not introduce unsafeness.

    这种协方差的一个后果是,当按值传递 Box<T> 时,我们被允许'忘记 T '的原始生命周期 . 但是这并没有引入不安全因素,因为当我们通过值传递时,我们保证在 Box<T> 被移动的位置没有 T 的其他用户 . 在旧的位置上没有其他人指望在移动之后保持这样的前一生命 T .

    但更重要的是, Box<T>T 上的协变性并没有引入不可靠性,因为 &mut Box<T>Box<T> 上是不变的,因此在 T 上不变 . 因此,类似于上面的 &mut T 讨论,我们无法通过忘记 T 的生命周期详细信息然后在之后修改它来执行 &mut Box<T> 的终生恶作剧 .

相关问题