[我正在研究Rust Book,对第4章中的清单感到惊讶:
fn main() {
let mut s = String::from("hello");
let r1 = &s;
let r2 = &s;
let r3 = &mut s;
println!("{}, {}, and {}", r1, r2, r3);
}
为什么Rust不允许对可变变量进行不变的引用?这似乎会削弱安全保证。如果我有一个可变的变量,并且将不可变的引用传递给某些线程,则这些线程假定值不会更改,但是我可以通过原始变量来对该值进行更改。
我还没有到达线程,但是发现这个奇怪,在这种情况下,与C ++没什么不同:
void doNotChangeMyString(const std::string& myConstString) {
// ... assume that myConstString cannot change and use it on a thread
// return immediately even though some worker thread is still
// using myConstString
}
void main() {
std::string s = "hello" // not const!
doNotChangeMyString(s);
s = "world"; // oops
}
项目的可变性本质上是rust变量名称的一部分。以下面的代码为例:
let mut foo = String::new();
let foo = foo;
let mut foo = foo;
[foo
突然变得不可变,但这并不意味着前两个foo
不存在。
另一方面,可变引用附加到对象的生存期,因此是类型绑定的,并且将存在其自身的生存期,如果没有访问原始对象,将不允许任何形式的访问通过参考。
let mut my_string = String::new();
my_string.push_str("This is ok! ");
let foo: &mut String = &mut my_string;
foo.push_str("This goes through the mutable reference, and is therefore ok! ");
my_string.push_str("This is not ok, and will not compile because `foo` still exists");
println!("We use foo here because of non lexical lifetime: {:?}", foo);
my_string.push_str
的第二次调用将不会编译,因为此后可以使用foo
(在这种情况下,可以保证使用)。
您的特定问题要求类似以下内容,但您甚至不需要多线程即可对此进行测试:
fn immutably_use_value(x: &str) {
println!("{:?}", x);
}
let mut foo = String::new();
let bar = &foo; //This now has immutable access to the mutable object.
let baz = &foo; //Two points are allowed to observe a value at the same time. (Ignoring `Sync`)
immutably_use_value(bar); //Ok, we can observe it immutably
foo.push_str("Hello world!"); //This would be ok... but we use the immutable references later!
immutably_use_value(baz);
This does not compile.如果您可以注释生命周期,它们看起来将类似于以下内容:
let mut foo = String::new(); //Has lifetime 'foo
let bar: &'foo String = &foo; //Has lifetime 'bar: 'foo
let baz: &'foo String = &foo; //Has lifetime 'baz: 'foo
//On the other hand:
let mut foo = String::new(); //Has lifetime 'foo
let bar: &'foo mut String = &mut foo; //Has lifetime 'bar: mut 'foo
let baz: &'foo mut String = &mut foo; //Error, we cannot have overlapping mutable borrows for the same object!
一些补充说明:
由于NLL,将编译以下代码:
let mut foo = String::new();
let bar = &foo;
foo.push_str("Abc");
因为可变使用bar
后未使用foo
。
您提到线程,它有其自身的约束和特征:
Send
特性将使您可以跨线程授予变量的所有权。
Send
特性将允许您跨线程共享对变量的引用。只要原始线程在借用期间不使用该对象,这将包括可变引用。
一些示例:
Sync
为Sync
,它可以跨线程发送并在线程之间共享T
为Send + Sync
,它可以在线程之间共享,但不能在线程之间发送。一个示例是只能在原始线程上销毁的窗口句柄。T
为!Send + Sync
,它可以跨线程发送,但不能在线程之间共享。一个示例是T
,由于它不使用原子(多线程安全组件),因此只能在单个线程上使用其运行时借用检查。Send + !Sync
类型为RefCell
,它只能存在于创建它的线程上。一个示例是RefCell
,它无法跨线程发送其自身的副本,因为它无法原子计数引用(请看T
来做到这一点),并且由于它没有生命周期,因此无法在跨线程发送时强制其自身的单个副本存在线程边界,因此不能跨线程发送。