记住前面关于引用的说明:
-
一个引用的作用域从声明的地方开始一直持续到最后一次使用为止
-
不能在拥有不可变引用的同时拥有可变引用
-
可变引用有一个很大的限制:在特定作用域中的特定数据只能有一个可变引用
第一条作用域容易理解,第二三条类似文件的读写锁,确保不能同时修改。
但下面关于slices的描述就有点难理解了。
先不管这啥迭代器用法,函数就返回usize普通类型,值拷贝,没有所有权问题。
fn first_word(s: &String) -> usize {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return i;
}
}
s.len()
}
fn main() {
let mut s = String::from("hello world");
let word = first_word(&s); // word 的值为 5
s.clear(); // 这清空了字符串,使其等于 ""
// word 在此处的值仍然是 5,
// 但是没有更多的字符串让我们可以有效地应用数值 5。word 的值现在完全无效!
}
“缺陷”在于无法确保返回的索引的有效性。从Rust的角度来看这是缺陷,而且想要去解决,而解决的方式就用到了所有权规则:引用和借用。
关于slices先来了解下
字符串字面值就是 slice
let s = String::from("hello world");
let hello = &s[0..5];
let world = &s[6..11];
let slice = &s[..2];
let slice = &s[3..];
let slice = &s[..]; //引用s所有字符
之前还在怀疑,就定义个let s = "Hello, world!";
又不在基本类型里面,鬼知道这个啥类型哦。这里说slices是&str
类型,注意:天生就是引用/借用类型,所有slices没有所有权也不难理解。
难点来了
fn main() {
let mut s = String::from("hello world");
let word = first_word(&s);
s.clear(); // 错误!
println!("the first word is: {}", word);
}
fn first_word(s: &String) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}
这里rust的引用/借用规则开始出手了,他说你违反了”当拥有某值的不可变引用时,就不能再获取一个可变引用“。之前跟我说这句话的时候是在同一个作用域同时定义了一个可变引用和不可变引用,那会还能理解,现在这情形怎么套上去,哎,年轻的RustBook,不讲武德,耗子尾汁。。我只能这么理解:s的不可变引用生命周期内,不能再通过其可变引用做修改(至于ta怎么检测到做没做修改,应该不是靠猜的)不信你看:
fn main() {
let mut s = String::from("hello world");
let word = first_word(&s);
//s.clear(); // 错误!
println!("the first word is: {}", word);
}
fn main() {
let mut s = String::from("hello world");
let word = first_word(&s);
println!("s = {}", s); // dei
println!("the first word is: {}", word); // dei
}
fn main() {
let mut s = String::from("hello world");
let word = first_word(&s);
println!("the first word is: {}", word);
s.clear(); // dei!
}
第一个,s.clear通过可变引用修改,但是发现还有s的不可变引用生命周期没结束,报错了
第二个,虽然在可变引用周期内,使用了不可变引用,但是没报错,注意没报错,回顾下文章开始的那条(不能在拥有不可变引用的同时拥有可变引用),难道只引用一部分,换了个马甲,这条规则就失效了吗,又或者slices发生了值拷贝?这又和slices天生是引用的&
矛盾呀。这里s是String::from("hello world")
的可变引用,word是其的不可变引用,竟然同时存在了,暂时是无法解释了。
第三个,把通过可变引用的修改放到不可变引用的生命周期外,如果第二个都没毛病,这个自然也没毛病。
接下来直接最终的完美版本:
fn main() {
let my_string = String::from("hello world");
// first_word 中传入 `String` 的 slice
let word = first_word(&my_string[..]);
let my_string_literal = "hello world";
// first_word 中传入字符串字面值的 slice
let word = first_word(&my_string_literal[..]);
// 因为字符串字面值 **就是** 字符串 slice,
// 这样写也可以,即不使用 slice 语法!
let word = first_word(my_string_literal);
}
fn first_word(s: &str) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}
顺理成章的,如果把my_string
改成mut
,然后在let word = first_word(&my_string[..]);
后面加个my_string.clear();
也没有问题,但是一旦在后面再使用word
编译器就不干了。
let mut my_string = String::from("hello world");
// first_word 中传入 `String` 的 slice
let word = first_word(&my_string[..]);
my_string.clear();
println!("word = {}", word); // 报错,
let my_string_literal = "hello world";
5 | let word = first_word(&my_string[..]);
| --------- immutable borrow occurs here
6 |
7 | my_string.clear();
| ^^^^^^^^^^^^^^^^^ mutable borrow occurs here
8 | println!("word = {}", word);
| ---- immutable borrow later used here
rust为了保证内存使用的安全性,更像是把c++高自由度的内存接口五花大绑,Rust以安全的名义把这些接口包起来,然后给你留条安全的路,如果你无法找到那条安全的路——写安全的代码,那就不让你写代码(连可变变量都不是想变就变的,即使你不变,加行打印还要看在不在可变引用生命周期内,我已经是个废程序员了)。