似乎u
,一个可变的借用,在
let v = &*u;
u
和v
都是不可变的借用引用,因此它们都是允许的。
use std::ascii::AsciiExt;
fn show(a: &str) {
println!("a={}", a);
}
fn main() {
let mut t = String::new();
t.push('s');
let u = &mut t;
u.make_ascii_uppercase(); // u is really mutable here
let v = &*u; // u became immutable to allow this?
show(u); // both u and v are now accessible!
show(v);
}
输出:
a=S
a=S
如果我尝试在
之后使用u
作为可变借用
show(v);
编译器会记得那个
let v = &*u;
真的不被允许:
cannot borrow `*u` as mutable because it is also borrowed as immutable
这是一个错误还是真的有些"当不再需要可变性时,会自动将可变借用转换为不可变的"原理?我正在使用Rust 1.13.0。
答案 0 :(得分:5)
一个可变的引用可以不可靠地借用,但这不是这里发生的事情。
在使用&
形成引用时,您需要明确可变性;除非你指定&mut
,否则它将是一个不可变的引用。
您的示例可以简化为:
use std::ascii::AsciiExt;
fn main() {
let mut t = "s".to_string();
let u = &mut t;
u.make_ascii_uppercase();
let v = &*u;
let () = v;
}
最后一行是让编译器告诉我们(在错误消息中)v
的类型是什么的技巧。它报道:
error[E0308]: mismatched types
--> <anon>:9:9
|
9 | let () = v;
| ^^ expected reference, found ()
|
= note: expected type `&std::string::String`
= note: found type `()`
我们有:
u
:一个不可变的绑定,是t
的可变借用v
:一个不可变的绑定,是t
到u
但是,如果我将v
行更改为let v = &mut *u;
,那么我会获得expected type '&mut std::string::String'
,然后我们会:
u
:一个不可变的绑定,是t
的可变借用v
:一个不可变的绑定,是t
到u
这里的重要概念是重新借用,这是&*u
和&mut *u
的内容。重新借用允许从现有参考中形成新的参考:
重新借用规则相对简单,它们反映了借用规则:
值得注意的是,重新借用的引用可以比其形成的引用更长寿命:
fn main() {
let mut t = "s".to_string();
let v;
{
let u = &mut t;
v = &mut *u;
}
v.make_ascii_uppercase();
show(v);
}
这是确保您可以从函数返回引用所必需的;当然。
因此,最终,重新借用会被编译器追溯到原来的借用值;但是,由于重新借用机制,它允许形成对此原始值的不可变引用,即使可变引用在范围内......并且只需确保此可变引用在新的不可变引用的生命周期内不可用。
当一个函数接受引用时,编译器会自动在调用站点引入一个具有适当可变性的重新借用;这是show
这里发生的事情:show(u)
实际上是show(&*u)
,在函数调用期间形成了一个新的不可变引用。
答案 1 :(得分:0)
首先,u
在任何时候都不可变,因为它是let u
声明的,而不是let mut u
。你可以改变它所指向的String
的原因是它拥有一个可变的引用; make_ascii_uppercase()
修改t
。
v
也是不可变的(mut
中没有let v
),因此当您调用show()
处理不可变引用时,不会违反借用规则 - 您可以一次执行多次不可变借用。
答案 2 :(得分:0)
这很令人困惑,所以让我们做一些实验。
您的代码编译:
let mut t = String::new();
t.push('s');
let u = &mut t;
u.make_ascii_uppercase(); // u is really mutable here
let v = &*u; // u became immutable to allow this?
show(u); // both u and v are now accessible!
show(v);
如果我们将let v
行更改为:
let v = &t;
错误[E0502]:不能将
t
借用为不可变因为它也被借用为可变- &GT; :12:14
好的,这是不同的。这告诉我&*u
,尽管与&t
的类型相同,但却不一样;前者是(子)借用 u
,但后者试图重新借用t
。
让我们尝试不同的实验。将前一行放回去,但现在添加一些新内容:
let v = &*u; // the original reborrow
let w = u; // Try to move out of `u`
错误[E0502]:不能将
t
借用为不可变因为它也被借用为可变- &GT; :12:14
啊哈!这证实v
确实是从u
借来的,而不是直接来自t
。
现在,在原文中,让我们通过u
添加一个尝试突变到最后:
let mut t = String::new();
t.push('s');
let u = &mut t;
u.make_ascii_uppercase(); // u is really mutable here
let v = &*u; // u became immutable to allow this?
show(u); // both u and v are now accessible!
show(v);
u.make_ascii_uppercase();
现在我明白了:
错误[E0502]:不能将
*u
借用为可变因为它也被借用为不可变的
我认为这基本上解释了发生了什么:
u
可疑地借用t
。这会直接阻止t
被访问。v
不可侵犯地借用u
。这意味着u
仍可以不可变地使用,但它不能被可变地使用或移出。{/ li>
u
存在v
时无法可靠地借用*u
,因此您也不能可变地使用var list = specs.Join(users,
s => s.lastupdatedby,
u => u.userid,
(s, u) => new { specs = s, users = u })
.Select(x => new {
specId = x.specs.specId,
desc = x.specs.desc,
createdby=x.specs.createdby,
username=x.users.username
}).ToString();
。 (这最后一点略显手淫;我欢迎进一步澄清......)