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

L.Y*_*Sim 9 generics polymorphism covariance rust

Rust Nomicon有一个关于方差的整个部分,除了这个关于Box<T>Vec<T>(共)变体的小部分之外我或多或少都能理解T.

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

令我困惑的是以下几行:

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

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

如果是这样,那么我只能通过可变引用在其中存储值的事实如何证明它们的(共)方差?我明白了什么(共)方差和具有它的好处Box<T>,Vec<T>等等,但我努力只看到能够通过可变引用和(共)方差的理由来存储值之间的联系.

另外,当我们初始化a时Box,是否在没有涉及可变引用的情况下将值移入框中?这是否与我们只能通过可变引用存储值的声明相矛盾?

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


2018年5月2日更新:

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

L.Y*_*Sim 1

自从在 Nomicon 存储库中打开该问题后,维护者对该部分进行了修订,我认为该修订已经更加清晰了。修订版已合并。我认为修订版已经回答了我的问题。

下面我简单总结一下我所知道的情况。

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

BoxVec是有趣的情况,因为它们是协变的,但您绝对可以在其中存储值!这就是 Rust 的类型系统让它比其他类型系统更聪明的地方。为了理解为什么拥有容器对其内容进行协变是合理的,我们必须考虑可能发生突变的两种方式:按值或按引用。

如果突变是按值进行的,那么记住额外细节的旧位置将被移出,这意味着它不能再使用该值。所以我们根本不需要担心有人记住危险的细节。换句话说,在按值传递时应用子类型会永远破坏细节。例如,这样可以编译并且没问题:

 fn get_box<'a>(str: &'a str) -> Box<&'a str> {
     // String literals are `&'static str`s, but it's fine for us to
     // "forget" this and let the caller think the string won't live that long.
     Box::new("hello") }
Run Code Online (Sandbox Code Playgroud)

如果突变是通过引用的,那么我们的容器将作为 传递&mut Vec<T>。但&mut对其值而言是不变的,因此&mut Vec<T>实际上对 而言是不变的T。因此,在通过引用进行变异时,T 上的协变这一事实 Vec<T>根本不重要。

&mut Vec<T>这里的关键点实际上是over的不变性和over 的T不变性之间的相似性。&mut TT

前面在修订后的注释部分解释了为什么将军&mut T不能在 上协变T&mut T借用T,但不拥有T,这意味着还有其他东西引用T并对其生命周期有一定的期望。

但是,如果我们被允许将&mut T协变传递给T,那么overwritenomicon 示例中的函数将显示我们如何不同位置(即在 的体内)中断T调用者位置中的生命周期。overwrite

从某种意义上说,允许T类型构造函数的协变允许我们T在传递类型构造函数时“忘记原始的生命周期”,并且这种“忘记原始生命周期T”是可以的,&T因为我们没有机会T通过它进行修改,但当我们拥有一个时,这是很危险的,&mut T因为我们有能力在忘记它的一生详细信息后进行修改 。这就是为什么需要在 上保持不变。T &mut TT

看起来这个nomicon试图表达的观点是:Box<T>协变是可以的T,因为它不会引入不安全性。

这种协变的后果之一是,我们在传递值时可以“忘记 的原始生命周期TBox<T>。但这并不会带来不安全性,因为当我们传递值时,我们保证在被移动的T位置不再有用户。Box<T>旧位置上没有其他人指望在搬迁T后仍保持这种状态。

但更重要的是,在对 进行可变引用时,Box<T>协变并不会带来不安全性,因为是 不变的,因此也是不变的。因此,与上面的讨论类似,我们无法通过忘记生命周期细节然后对其进行修改来执行生命周期恶作剧。TBox<T>&mut Box<T>Box<T>T&mut T&mut Box<T>T