RUST NOMICON具有整个方差的部分,除了Box<T>
和Vec<T>
的这个小部分,我或多或少地理解了T
上的(CO(变体。
Box
和Vec
是有趣的情况,因为它们的变体,但是您绝对可以将值存储在其中!这是Rust变得非常聪明的地方:对他们来说,变体是可以的,因为您只能通过可突出的参考将其存储在它们中的中!可变的参考使整个类型不变,因此阻止您将短暂的类型走私到其中。
让我感到困惑的是以下行:
可以使他们成为变体是可以的,因为您只能通过可变的参考!
存储中的值
我的第一个问题是,我对可变参考的意思有些困惑。它是对Box
/Vec
的可变引用吗?
如果是这样,我只能通过可变参考来存储它们的值的事实证明其(CO(差异是合理的?我了解(CO(方差是什么以及为Box<T>
,Vec<T>
等拥有它的好处,但是我正在努力看到仅通过可变引用来存储值与(CO(方差的正当值之间的链接。p>同样,当我们初始化 Box
时,如果不涉及可变参考的情况,则值不是将值移入框中吗?这是否与我们只能通过可变参考存储在其中值的陈述不矛盾?
最后,在哪个上下文中,借用了"可变参考"?它们是指调用修改Box
或Vec
的方法时,您会隐式服用&mut self
?是提到的可变参考吗?
更新2018年5月2日:
由于我还没有对这个问题获得令人满意的答案,所以我认为提名的解释确实令人困惑。因此,正如下面的评论主题中所承诺的那样,我在Rust Nomicon存储库中开了一个问题。您可以跟踪那里的任何更新。
我认为该部分可以使用一些工作来清晰。
我对可变参考的内容有些困惑。它是对盒子/vec的可变引用吗?
否。这意味着,如果将值存储在现有的 Box
中,则必须通过对数据的可变引用来执行此操作,例如使用Box::borrow_mut()
。
本节试图传达的主要思想是,您不能修改 Box
的内容,而另有提到了内容。这是可以保证的,因为Box
拥有其内容。为了更改Box
的内容,您必须通过获取新的可变参考。
这意味着 - 即使您确实以较短的价值覆盖了内容,也没关系,因为没有人可以使用旧值。借用检查器不允许它。
这与函数参数不同,因为一个函数具有一个代码块,该代码块实际上可以用其参数来完成事情。对于Box
或Vec
,您必须通过对它们做任何事情,然后才能借用它们。提名:
Box和VEC是有趣的情况,因为它们的变体,但是您绝对可以在其中存储值!这是Rust变得非常聪明的地方:对他们来说,变体是可以的,因为您只能通过可变的参考来存储它们的值!可变的参考使整个类型不变,因此阻止您将短暂的类型走私到其中。
考虑添加值的Vec
方法:
pub fn push(&'a mut self, value: T)
自我的类型是 &'a mut Vec<T>
,我知道这是可见参考的提名正在谈论的是,因此对Vec
案例进行实例化上述短语的最后一句话:
类型&'a mut Vec<T>
是不变的,因此阻止您将短暂的类型走私到Vec<T>
中。
框相同的推理。
以另一种方式说:尽管Vec
和Box
是变体,Vec
和Box
所包含的值始终超过容器,因为您只能通过可突出的参考将其存储在它们中。
考虑以下片段:
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)
的相似性。
以来,自提名库中打开问题以来,维护者已经对该部分进行了修订,我认为这很清楚。修订已合并。我认为我的问题是通过修订回答的。
下面我提供了我所知道的简要摘要。
与我的问题有关的部分如下(重点是我的(:
Box
和Vec
是有趣的情况,因为它们协变了,但是你 绝对可以将价值存储在其中!这是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") }
如果突变是引用,则我们的容器作为
&mut Vec<T>
传递。但是&mut
在其上不变 值,因此&mut Vec<T>
实际上是在T
上不变的。所以事实Vec<T>
在突变时对T的协变量无关紧要 参考。
这里的要点确实是T
上&mut Vec<T>
的不变性与T
上的不变性&mut T
之间的平行。
在修订的名称部分中进行了早期解释,为什么一般&mut T
不能在T
上进行协变。&mut T
借用T
,但它不拥有T
,这意味着还有其他内容是指T
,并且对其寿命有一定的期望。
但是,如果我们被允许通过 &mut T
通过T
通过CC_49,则名nICON示例中的overwrite
功能显示了我们如何在呼叫者的位置中打破T
的寿命 一个不同的位置(即overwrite
的主体(。
从某种意义上说,允许在T
上使用类型构造函数的协方差使我们可以在传递类型构造函数时"忘记T
的原始寿命",并且"忘记T
的原始寿命"是可以的,因为没有&T
,因为没有我们可以通过它修改T
的机会,但是当我们拥有&mut T
时,这很危险,因为我们有能力修改 T
忘记了有关它的终身详细信息。这就是为什么&mut T
需要在T
上不变。
似乎提名要提出的重点是:Box<T>
可以通过T
进行协变,因为它不引入不安全。
这种协方差的后果之一是,当按值传递Box<T>
时,我们可以忘记"忘记T
的原始寿命"。但这不会引入不安全,因为当我们按价值传递时,我们保证在Box<T>
移动的位置中没有其他T
用户。旧位置中没有其他人依靠T
的前一生,以免移动后。
,但更重要的是,在T
上是协变量的Box<T>
在对Box<T>
进行可变的参考时不会引入不安全,因为&mut Box<T>
在Box<T>
上是不变的,因此在T
上是不变的。因此,类似于上面的&mut T
讨论,我们无法通过忘记有关T
的终身详细信息,然后在以后对其进行修改。
我想重点是,虽然您可以将Box<&'static str>
转换为Box<&'a str>
(因为Box<T>
是协变量(,但您不能将&mut Box<&'static str>
转换为&mut Box<&'a str>
(因为&mut T
是不变的(。