rust中&與ref
阿新 • • 發佈:2021-06-20
&操作符
&操作符用於變數名前,表示建立此變數的引用,用於型別名前,表示此型別的借用型別。
let x = vec![1, 2, 3]; // vec不具備複製特性。賦值時不會自動複製。
let y = &x; // &x建立對x的引用,再繫結到y,y的型別是&Vec<i32>。
let z: &Vec<i32> = &x; //等同上一行
fn some_fun(arg: &T) {} // &T是一個型別,表示arg是對T型別的引用。
當&出現在=左側時,可以用於模式匹配,但通常這種寫法都是錯的,僅在具備複製特性的型別時才能通過編譯。
具備複製特性的型別
// 具備複製特性的型別,如i32
let x = 100;
let &y = &x; // &x建立對x的引用(&i32型別),再通過模式匹配,與y進行繫結。
// 用&y匹配&i32型別,同時將=右側解構,得到&i32型別,相當於將右側的i32複製到左側的i32。
// 因為i32可以複製,所以可以成功繫結。
不具備複製特性的型別
// 不具備複製特性的型別,如Vec<i32> let x = vec![1, 2, 3]; let &y = &x; // 出錯,&x建立對x的引用(&Vec<i32>型別),再通過模式匹配,與y進行繫結。 // 繫結時&y匹配&Vec<i32>型別,所以y就是Vec<i32>型別,=右側也對&x的型別進行解構,也得到&Vec<i32>, // 於是這上繫結就相當於將右側的Vec<i32>賦給左側的Vec<i32>, // 因為Vec<i32>不能複製,所以這裡是移動語義,相當於把x移動到y。 // 但因為&x本身建立了對x的引用(臨時的引用),不能移動x。出錯資訊: // error[E0507]: cannot move out of a shared reference // --> src/bin/test1.rs:11:14 // | // 11 | let &u = &x; // | -- ^^ // | || // | |data moved here // | |move occurs because `u` has type `Vec<i32>`, which does not implement the `Copy` trait // | help: consider removing the `&`: `u`
上述出錯的程式碼相當於:
let x = vec![1, 2, 3];
let y = &x;
let &z = y; // 對y進行解構,得到&Vec<i32>,於是嘗試將y所指向的x移動到z,但因為y的存在不能成功。
ref關鍵字
某些情況下,使用ref與&是等效的,但ref關鍵字一般用於要繫結的識別符號前,通常這些地方都無法使用&。
let x = vec![1, 2, 3]; let ref y = x; // ref表示以引用的方式繫結y和x,所以y的型別是&Vec<i32>。 let y = &x; // 與上一句等價。
ref用於match的分支匹配中
match在匹配時,會拿走被匹配變數,所以match之後,原變數無法再使用。
let a = Some(vec![1, 2, 3]);
match a {
None => (),
Some(value) => println!("{:?}", value), // value繫結是移動語義,所以a被match佔據。
}
println!("{:?}", a); // 不能再使用a
要避免這種情況,可以使用ref,
let a = Some(vec![1, 2, 3]);
match a {
None => (),
Some(ref value) => println!("{:?}", value), // value以引用方式繫結。
}
println!("{:?}", a); // a仍然在
// 也可以寫成
let a = Some(vec![1, 2, 3]);
match &a { // 建立a的引用,match只使用了a引用,且match結束時,引用離開作用域也會銷燬
None => (),
Some(value) => println!("{:?}", value), // 編譯器會識別出,value也是引用。
}
println!("{:?}", a); // 可以繼續使用a,如果a可變,也可以修改a
ref用於解構時引用結構體某些欄位
不考慮複製特性,結構體進行匹配時,預設將使用移動語義,結構體欄位所有權從結構體移動到被繫結的變數上,原變數失去所有權。例如
#[derive(Debug)]
struct Person {
name: String,
}
fn main() {
let mut p = Person {
name: "薛海舟".to_string(),
};
let Person { name: a } = p; // a是被繫結的識別符號,這裡將p中name的所有權轉移給a變數。
p.name.push_str("舒珊靖"); // 失敗,因為p已經被移動。
println!("{:?}", a); // a中內容是"薛海舟"
}
使用ref就表示,要以引用的形式來繫結。例如
#[derive(Debug)]
struct Person {
name: String,
}
// ref只能用於要被繫結的識別符號前
fn main() {
let mut p = Person {
name: "薛海舟".to_string(),
};
let Person { name: ref mut a } = p; // a是是對p中name欄位的可變借用
*a = "this one".to_string(); // 對a賦值,就相當於對p中name操作
println!("舒珊靖", p); // p中name變為舒珊靖
}