在之前的所有權章節有提到以下的 code 行為是 move 而非複製
let hello = "Hello World".to_string();
let hello2 = hello; // hello move to hello2
在 Rust 中可以定義 function
fn add(v1: i32, v2: i32) -> i32{
return v1 + v2;
}
在 move 的規則之下,在需要保留原本傳入參數的情況下。就得將原本的參數再回傳回來
fn concat(s1: String, s2: String) -> (String, String, String) {
let result = format!("{}{}", s1, s2);
return (s1, s2, result);
}
fn main() {
let hello = "Hello".to_string();
let world = "World".to_string();
let (hello, world, hello_world) = concat(hello, world);
println!("{} = {} + {}", hello_world, hello, world);
}
這種作法顯然會帶來兩個問題
- 撰寫麻煩
- 額外的 Stack 複製成本
為此 Rust 中引入了 reference 機制,以下為使用 Reference 的機制改寫的 code
fn concat(s1: &String, s2: &String) -> String {
return format!("{}{}", s1, s2);
}
fn main() {
let hello = "Hello".to_string();
let world = "World".to_string();
let hello_world = concat(&hello, &world);
println!("{} = {} + {}", hello_world, hello, world);
}
在上述的範例中,主要做了兩項改變
- concat 參數的型別由
String變為&String - concat 傳入的變數由
hello變為&hello
&String翻譯為中文的話是 "一個 Reference,指向的型別是 String"
&hello的話,& 則是一個運算子,對 hello 運算後會得到指向 hello 的 Reference
以下的 code 我們可以得到 String 與 &String 在記憶體中的大小
fn main() {
println!("{}", std::mem::size_of::<String>());
println!("{}", std::mem::size_of::<&String>());
}
在 x64 環境下,String 大小是 24 bytes,&String 大小則是 8 bytes,而 x86 環境下則分別是 12 bytes 與 4 bytes
可以發現 reference 的大小規則與所在平台的記憶體空間的所需定位大小一致,基本上就是 C 的指標
一旦有了指標出現,就會想到一些安全問題,比如 Dangling Pointer等,在之後的文章會介紹 Rust 如何防止 Rerference 的問題
沒有留言:
張貼留言