23.所有权.rs
/* 所有權
1.Rust 中的每個值都有一個變量,稱為其所有者。
2.一次只能有一個所有者。
3.當所有者不在程序運行范圍時,該值將被刪除。變量與數據交互方式主要有移動(Move)和克隆(Clone)兩種。可以引用租借*/fn main() {{// 在聲明以前,變量 s 無效let _s = "runoob";// 這里是變量 s 的可用范圍}// 變量范圍已經結束,變量 s 無效test1();}fn test1(){let x = 5;let y = x;println!("{} -- {}",x,y);/*這個程序將值 5 綁定到變量 x,然后將 x 的值復制并賦值給變量 y。現在棧中將有兩個值 5。此情況中的數據是"基本數據"類型的數據,不需要存儲到堆中,僅在棧中的數據的"移動"方式是直接復制,這不會花費更長的時間或更多的存儲空間基本數據"類型有這些:所有整數類型,例如 i32 、 u32 、 i64 等。布爾類型 bool,值為 true 或 false 。所有浮點類型,f32 和 f64。字符類型 char。僅包含以上類型數據的元組(Tuples)。*/let x = "12332";let y = x;println!("{} -- {}",x,y);//有點奇怪,x為啥沒有失效?/*兩個 String 對象在棧中,每個 String 對象都有一個指針指向堆中的 "hello" 字符串。在給 s2 賦值時,只有棧中的數據被復制了,堆中的字符串依然還是原來的字符串。前面我們說過,當變量超出范圍時,Rust 自動調用釋放資源函數并清理該變量的堆內存。但是 s1 和 s2 都被釋放的話堆區中的 "hello" 被釋放兩次,這是不被系統允許的。為了確保安全,在給 s2 賦值時 s1 已經無效了。沒錯,在把 s1 的值賦給 s2 以后 s1 將不可以再被使用s1 名存實亡。*/let s1 = String::from("hello");let s2 = s1;//println!("{} -- {}",s1,s2);//這種寫法就可以失效了?///克隆/*Rust會盡可能地降低程序的運行成本,所以默認情況下,長度較大的數據存放在堆中,且采用移動的方式進行數據交互。但如果需要將數據單純的復制一份以供他用,可以使用數據的第二種交互方式——克隆。*/let s1 = String::from("hello");let s2 = s1.clone();println!("s1 = {}, s2 = {}", s1, s2);//涉及函數的所有權機制let s = String::from("hello");// s 被聲明有效takes_ownership(s);// s 的值被當作參數傳入函數// 所以可以當作 s 已經被移動,從這里開始已經無效// println!("{}",s); 錯誤!let x = 5;// x 被聲明有效makes_copy(x);// x 的值被當作參數傳入函數// 但 x 是基本類型,依然有效// 在這里依然可以使用 x 卻不能使用 slet s1 = gives_ownership();// gives_ownership 移動它的返回值到 s1println!("{}",s1);let s2 = String::from("takes_and_gives_back");// s2 被聲明有效let s3 = takes_and_gives_back(s2);// s2 被當作參數移動, s3 獲得返回值所有權println!("{}",s3);//測試引用testreference();testreference2();testreference3();testreference4();//let reference_to_nothing = testreference5();}// 函數結束, x 無效, 然后是 s. 但 s 已被移動, 所以不用被釋放//如果將變量當作參數傳入函數,那么它和移動的效果是一樣的。
fn takes_ownership(some_string: String) { // 一個 String 參數 some_string 傳入,有效println!("{}", some_string);
} // 函數結束, 參數 some_string 在這里釋放fn makes_copy(some_integer: i32) { // 一個 i32 參數 some_integer 傳入,有效println!("{}", some_integer);
} // 函數結束, 參數 some_integer 是基本類型, 無需釋放fn gives_ownership() -> String {let some_string = String::from("gives_ownership");// some_string 被聲明有效return some_string;// some_string 被當作返回值移動出函數
}fn takes_and_gives_back(a_string: String) -> String { // a_string 被聲明有效a_string // a_string 被當作返回值移出函數
}fn testreference(){let s1 = String::from("testreference");let s2 = &s1;println!("s1 is {}, s2 is {}", s1, s2);/*& 運算符可以取變量的"引用"。當一個變量的值被引用時,變量本身不會被認定無效。因為"引用"并沒有在棧中復制變量的值引用不會獲得值的所有權。引用只能租借(Borrow)值的所有權。引用本身也是一個類型并具有一個值,這個值記錄的是別的值所在的位置,但引用不具有所指值的所有權:*/let s1 = String::from("calculate_length");let len = calculate_length(&s1);println!("The length of '{}' is {}.", s1, len);
}fn calculate_length(s: &String) -> usize {s.len()
}fn testreference2(){// let s1 = String::from("hello");// let s2 = &s1;// let s3 = s1;// println!("{}", s2);//這段程序不正確:因為 s2 租借的 s1 已經將所有權移動到 s3,所以 s2 將無法繼續租借使用 s1 的所有權。如果需要使用 s2 使用該值,必須重新租借:let s1 = String::from("testreference2");let mut s2 = &s1;let s3 = s2;s2 = &s3; // 重新從 s3 租借所有權println!("{}", s2);
}fn testreference3(){//如果嘗試利用租借來的權利來修改數據會被阻止:// let s1 = String::from("testreference3");// let s2 = &s1; // println!("{}", s2);// s2.push_str("oob"); // 錯誤,禁止修改租借的值// println!("{}", s2);//當然,也存在一種可變的租借方式,就像你租一個房子,如果物業規定房主可以修改房子結構,房主在租借時也在合同中聲明賦予你這種權利,你是可以重新裝修房子的let mut s1 = String::from("testreference3");// s1 是可變的let s2 = &mut s1;// s2 是可變的引用s2.push_str("oob");println!("{}", s2);}fn testreference4(){//可變引用與不可變引用相比除了權限不同以外,可變引用不允許多重引用,但不可變引用可以:// let mut s = String::from("hello");// let r1 = &mut s;// let r2 = &mut s;// println!("{}, {}", r1, r2);//Rust 對可變引用的這種設計主要出于對并發狀態下發生數據訪問碰撞的考慮,在編譯階段就避免了這種事情的發生。//由于發生數據訪問碰撞的必要條件之一是數據被至少一個使用者寫且同時被至少一個其他使用者讀或寫,所以在一個值被可變引用時不允許再次被任何引用。
}//垂懸引用(Dangling References)
// fn testreference5() -> &String {
// let s = String::from("hello");
// &s
// /*
// 這是一個換了個名字的概念,如果放在有指針概念的編程語言里它就指的是那種沒有實際指向一個真正能訪問的數據的指針(注意,不一定是空指針,還有可能是已經釋放的資源)。它們就像失去懸掛物體的繩子,所以叫"垂懸引用"。
// "垂懸引用"在 Rust 語言里不允許出現,如果有,編譯器會發現它。// 很顯然,伴隨著 dangle 函數的結束,其局部變量的值本身沒有被當作返回值,被釋放了。但它的引用卻被返回,這個引用所指向的值已經不能確定的存在,故不允許其出現。
// */
// }
?
總結
- 上一篇: 22.循环控制.rs
- 下一篇: 24.内存操作Copy-Move-Clo