所有权
所有权是 rust 这门语言的独特特性,其他语言是此设计。因为它是新的东西,所以我们需要去适应。
所有权与堆栈
像 JS 这种动态编程语言,通常不用直接接触堆栈。但是 rust 作为系统编程语言,对内容的控制是要精确到堆栈的。所有权其实也是对堆栈内存控制的一个体现。
所有权规则
所有权只是规则,作用于值和变量上。下面是所有权的规则:
- 变量(所有者)与值必须有对应关系,变量就是值的 owner(主人)
- 所有者只能有一个
- 范围(作用域)内有效,超出无效
rust 范围
范围一般使用 {} 包裹:
//
let s = "string";
// {} 范围
{
let a = "this is a";
}
// s 在当前访问范围内
// a 不在当前访问内,不能使用,超出它能使用的范围,此时内部已经将其删除。
赋值,移动与变量类型所有权
赋值
赋值只是正对基本数据类型
- str
- 所有整形
- 布尔类型
- 浮点
- 元组
基础类型字符串与所有
let str = "this";
let o_str = str;
println!("{}-{}", str, o_str); // this-this
str 是一个简单的切片类型,我们 str 直接赋值给 o_str, 没有任何的问题。
引用类型字符串与所有权
let str_ref = String::from("this is ref string");
println!("{}-{}", str_ref, o_str);
let o_str_ref = str_ref; // 引用类型数据赋值,str_ref 对应的值的所有权发生了变化
println!("{}-{}", o_str_ref, str_ref); // str_ref 此时已经没有所有权对应的值
对于 str_ref 变量, rust 编译器会认为其值发生了移动 (move),而没有所有权。
clone 克隆,解决 move 的没有所有权的部分问题
上面我们其实是使用了转移 move,但是 rust 语言的所有权的设计, str_ref 已经发生了转移,为了也能访问,其实 rust 的 String 给我们提供了 clone 方法来解决这个问题
let str_ref = String::from("this is ref string");
println!("{}-{}", str_ref, o_str);
let o_str_ref = str_ref.clone(); // clone 相当于单独的 clone 一个新的内容,到新的地址
println!("{}-{}", o_str_ref, str_ref); // str_ref 此时已经没有所有权对应的值
函数参数-函数返回值转移所有权
转移有一个规则:
将值分配给另一个变量会移动它。当包含堆上数据的变量超出范围时,该值将被清除,
drop除非该数据已被移至由另一个变量拥有。
传入参数
一个变量通过参数,传递所有权后,变量的所有权归函数内部的变量所有权,在没有返回值时,在函数外部已经访问该参数的所有权。
fn main() {
let val = String::from("this is val"); // val 获取 this is val 所有权
fn get_param_val_ownership(val){
println!("{}", val);
}
get_param_val_ownership(val);
// val is no in scope, because is no fn get_param_val_ownership
println!("{}", val) // error
}
错误还和上面的图片一致:
这个问题会在下面的引用和借用有了解
函数返回值
函数的返内部的变量具有的返回值,函数的返回值赋值给函数外部变量新的
fn return_val_ownership() -> String {
let c = String::from("this is return val ownership");
return c;
}
let outer_c = return_val_ownership();
println!("{}", outer_c); // outer_c get the c val ownership
不获取的所有权操作:引用和借用
其实前面报错中,我们也发现了 borrowed 的相关错误。下面从引用开始,在到借用的相关所有权。
fn borrow_fn() {
let val = String::from("this is test borrow");
let len = borrow_fn_t(&val); // 这里我们借用 val,并没有将所有权通过参数传递到函数内部
println!("{}-{}", val, len); // 借用,使得 val 还能够访问
}
fn borrow_fn_t(val: &String) -> usize {
val.len()
}
borrow_fn_t(&val) 中 & 即引用操作符,用于表示引用了 val 变量。
引用和可变引用
&String&mut String
不可变引用
fn change_val_ref_error(val: &String) {
return val.push_str("sfdf"); // 这里要特别注意借用过来的 val 是不能修该的
}
可变引用
为了能够需改数据,我们的数据必须是 mut 不可变的,而且 &mut 是
fn main() {
let mut s = String::from("hello");
change(&mut s);
}
fn change(some_string: &mut String) {
some_string.push_str(", world");
}
可变的引用 &mut:一次只能有一个对特定数据的可变引用
let mut s = String::from("hello");
let r1 = &s; // no problem
let r2 = &s; // no problem
let r3 = &mut s; // BIG PROBLEM
println!("{}, {}, and {}", r1, r2, r3);
悬空引用
fn m_t() {
let ref_to_nothing = return_ref_s();
}
fn return_ref_s() -> &String {
let s = String::from("this is string");
&s // 这里的 &s, 是返回值,但是这个引用没有被使用到,处于悬垂的作用
}
无所有权类型总结
- 切片类型 slice 无所有权