Rust 学习笔记:修复所有权常见错误
- [Rust 学习笔记:修复所有权常见错误](#Rust 学习笔记:修复所有权常见错误)
Rust 学习笔记:修复所有权常见错误
错误一:返回栈上的引用
错误代码:
rust
fn return_a_string() -> &String {
let s = String::from("Hello World!");
&s
}
fn main() {
let value = return_a_string();
println!("{}", value);
}
错误原因:
在 return_a_string 函数中,s 的生命周期只持续到函数结束。value 将指向一块未定义的内存。
解决方法 1:
在 Rust 中,数据必须比它的引用活的时间长。return_a_string 函数不应该返回引用,而应该转移所有权。
rust
fn return_a_string() -> String {
let s = String::from("Hello World!");
s
}
fn main() {
let value = return_a_string();
println!("{}", value);
}
解决方法 2:添加生命周期说明符
rust
fn return_a_string() -> &'static str {
"Hello World!"
}
fn main() {
let value = return_a_string();
println!("{}", value);
}
static 表示字符串在整个程序运行期间一直存在。
错误二:没有足够的权限
错误代码:
rust
fn stringify_name_with_title(name: &Vec<String>) -> String {
name.push(String::from("Esq."));
let full_name = name.join(" ");
full_name
}
fn main() {
let name = vec![String::from("Ferris")];
let first = &name[0];
stringify_name_with_title(&name);
println!("{}", first);
}
错误原因:
stringify_name_with_title 函数的入参 name 是一个不可变变量,没有修改权限。
解决方法:创建一个可变变量
rust
fn stringify_name_with_title(name: &Vec<String>) -> String {
let mut full_name = name.join(" ");
full_name.push_str(" Esq.");
full_name
}
fn main() {
let name = vec![String::from("Ferris")];
let full = stringify_name_with_title(&name);
println!("{}", full);
}
错误三:别名和可变性
错误代码:
rust
fn add_big_strings(dst: &mut Vec<String>, src: &[String]) {
let largest: &String = dst.iter().max_by_key(|l| l.len()).unwrap();
for s in src {
if s.len() > largest.len() {
dst.push(s.clone());
}
}
}
错误原因:
原本 dst 是一个可变的引用,但 largest 作为一个不可变的引用指向了 dst 中的一个元素。后面 push 就会发生错误,因为暂时没有 dst 的写权限了。
解决方法:
不创建有关 dst 的不可变引用了。
rust
fn add_big_strings(dst: &mut Vec<String>, src: &[String]) {
let largest_len = dst.iter().max_by_key(|l| l.len()).unwrap().len();
for s in src {
if s.len() > largest_len {
dst.push(s.clone());
}
}
}
错误四:从集合中拷贝一个元素(转移所有权)
错误代码:
rust
fn main() {
let v: Vec<i32> = vec![0, 1, 2];
let n_ref: &i32 = &v[0];
let n: i32 = *n_ref;
let v: Vec<String> = vec![String::from("Hello world")];
let s_ref: &String = &v[0];
let s: String = *s_ref;
}
错误原因:
不可以通过引用来获得所有权!
i32 具有 Copy Trait,在解引用的时候完成了复制。
String 没有 Copy Trait,在解引用时尝试获得所有权,失败。
解决方法:
通过 clone 创建一个字符串的副本。
rust
fn main() {
let v: Vec<i32> = vec![0, 1, 2];
let n_ref: &i32 = &v[0];
let n: i32 = *n_ref;
let v: Vec<String> = vec![String::from("Hello world")];
let s_ref: &String = &v[0];
let s: String = v[0].clone();
}
错误五:对元组的引用
错误代码:
rust
fn get_first(name: &mut (String, String)) -> &String {
&name.0
}
fn main() {
let mut name = (String::from("A"), String::from("B"));
let first = get_first(&mut name);
name.1.push_str(", Esq.");
println!("{first} {}", name.1);
}
错误原因:
Rust 只关注函数的声明那一行,它发现 get_first 函数返回的是一个不可变的引用,它并不关心引用的是 name.0 还是 name.1,将 name.0 和 name.1 的写权限都暂时剥夺了,name.1 就不能修改了。
解决方法:
直接引用 name.0,因为元组中的每个元素的所有权都是独立的,这里只是将 name.0 和 name 的写权限暂时剥夺了,只修改 name.1 是不会报错的。
rust
fn main() {
let mut name = (String::from("A"), String::from("B"));
let first = &name.0;
name.1.push_str(", Esq.");
println!("{first} {}", name.1);
}
错误六:修改数组中的元素
错误代码:
rust
fn main() {
let mut a = [0, 1, 2, 3];
let x = &mut a[1];
*x += 1;
let y = &a[2];
*x += *y;
println!("{a:?}");
}
错误原因:
对 a 中元素创建了一个可变引用后,a 就暂时失去了读、写、拥有权限,只有 x 有权限。后面想再创建一个对 a 中元素的不可变引用,y 又想要获取 a 的读权限,就出错了。
修改:
rust
fn main() {
let mut a = [0, 1, 2, 3];
let x = &mut a[1];
*x += 1;
// let y = &a[2];
// *x += *y;
println!("{a:?}");
}