所有权
🪄

所有权

创建时间
Oct 20, 2024 11:59 AM
上次编辑时间
Last updated November 19, 2024
标签
rust
所有权

堆与栈的区别

  • 在栈上分配内存比在堆上分配内存要快,因为入栈时操作系统无需进行函数调用(或更慢的系统调用)来分配新的空间,只需要将新数据放入栈顶即可。相比之下,在堆上分配内存则需要更多的工作,这是因为操作系统必须首先找到一块足够存放数据的内存空间,接着做一些记录为下一次分配做准备,如果当前进程分配的内存页不足时,还需要进行系统调用来申请更多内存。 因此,处理器在栈上分配数据会比在堆上分配数据更加高效

所有权规则

💡
  1. Rust 中每一个值都被一个变量所拥有,该变量被称为值的所有者。
  1. 一个值同时只能被一个变量所拥有,或者说一个值只能拥有一个所有者。
  1. 当所有者(变量)离开作用域范围时,这个值将被丢弃(drop)。
  • 例子
// -当所有权转移时,可变性也可以随之改变。 s已经失效。 fn main() { let s = String::from("hello, "); let mut s1 = s; s1.push_str("world") } // 引用:s1,s2分别借用了t的两个值但是并没有获取所有权。 fn main() { let t = (String::from("hello"), String::from("world")); // 也可以 let (s1, s2) = &t; let (s1, s2) = (&t.0, &t.1); println!("{:?}, {:?}, {:?}", s1, s2, t); // -> "hello", "world", ("hello", "world") }
  • 变量失效的时机。对比Java,Java中是两个变量指向一个堆分配对象,并且通过这两个变量都可以进行对堆内的访问,而Rust仅保留了最后一个对象引用,屏蔽了之前的引用。
  • 以下变量又可以划分为:所有权型变量s和引⽤型变量r1,r2,r3。
fn main() { let mut s = String::from("hello"); let r1 = &s; let r2 = &s; println!("{} and {}", r1, r2); // 新编译器中,r1,r2作用域在这里结束 let r3 = &mut s; println!("{}", r3); } // 老编译器中,r1、r2、r3作用域在这里结束 // 新编译器中,r3作用域在这里结束
  • 默认做复制所有权的操作的有 7 种
    • 所有的整数类型
    • 布尔类型
    • 浮点数类型
    • 字符类型
    • 由以上类型组成的元组类型 tuple
    • 由以上类型组成的数组类型 array
    • 不可变引⽤类型 &

所有权

  • 引⽤的最后⼀次调⽤时机很关键,⼀个所有权型变量的作⽤域是从它定义时开始到花括号结束。⽽引⽤型变量的作⽤域不是这样,引⽤型变量的作⽤域是从它定义起到它最后⼀次使⽤时结束。
  • ⼀个所有权型变量的可变引⽤与不可变引⽤的作⽤域不能交叠,也可以说不能同时存在。
// error[E0502]: cannot borrow `a` as mutable because it is also borrowed as immutable // --> src/main.rs:13:17 // | // 12 | let mut a_mut = &a; // | -- immutable borrow occurs here // 13 | let a_st = &mut a; // | ^^^^^^ mutable borrow occurs here // 14 | println!("a_mut {}", a_mut); // | ----- immutable borrow later used here fn main() { let mut a = 123; let mut a_mut = &a; let a_st = &mut a; println!("a_mut {}", a_mut); println!("a_st {}", a_st); } // 而下面的交换输出顺序,则运行正确 // println!("a_mut {}", a_mut);使用后 a_mut 已经结束,则不影响a_st的使用。即作⽤域不能交叠。 fn main() { let mut a = 123; let mut a_mut = &a; println!("a_mut {}", a_mut); let a_st = &mut a; println!("a_st {}", a_st); }
  • 同⼀个所有权变量的可变引用作用域不可交叠,就是防止在交叠部分互相有可能发生变量的修改。
// error[E0499]: cannot borrow `a` as mutable more than once at a time // --> src/main.rs:4:22 // | // 3 | let mut a_mut1 = &mut a; // | ------ first mutable borrow occurs here // 4 | let mut a_mut2 = &mut a; // | ^^^^^^ second mutable borrow occurs here // 5 | println!("a_mut2 {}", a_mut2); // 6 | println!("a_mut1 {}", a_mut1); // | ------ first borrow later used here fn main() { let mut a = 123; let mut a_mut1 = &mut a; let mut a_mut2 = &mut a; //-> 防止在这跟下面对a_mut2输出中间对a_mut1做改变。 println!("a_mut2 {}", a_mut2); //-> 进而可能会导致程序的不正确。因为两个都是对a的可变引用 println!("a_mut1 {}", a_mut1); } // 修改如下则正常。 fn main() { let mut a = 123; let mut a_mut1 = &mut a; println!("a_mut1 {}", a_mut1); let mut a_mut2 = &mut a; println!("a_mut2 {}", a_mut2); }
  • 在有借⽤的情况下,不能对所有权变量进⾏更改值的操作。
// 正常 fn main() { let mut a = 123; let a_mut = &mut a; // 因为这一行a_mut是最后的使用结束,后面对a更改则对a_mut没影响 a = 321; println!("{}", a) } // error[E0506]: cannot assign to `a` because it is borrowed // --> src/main.rs:4:5 // | // 3 | let a_mut = &mut a; // | ------ `a` is borrowed here // 4 | a = 321; // | ^^^^^^^ `a` is assigned to here but it was already borrowed // 5 | println!("{}", a_mut) // | ----- borrow later used here fn main() { let mut a = 123; let a_mut = &mut a; a = 321; println!("{}", a_mut) // 在有借⽤的情况下,不能对所有权变量进⾏更改值的操作 } // 更别提a_mut对a是不可变引用了,下面也报错。 fn main() { let mut a = 123; let a_mut = &a; a = 321; // 在有借⽤的情况下,不能对所有权变量进⾏更改值的操作 println!("{}", a_mut) }
  • 某一时刻对一个所有权型变量只能存在⼀个可变引⽤,不能有超过⼀个可变借⽤同时存在,也可以说,对同⼀个所有权型变量的可变借⽤之间的作⽤域不能交叠。
// 这正常是因为a_mut在其定义的那一行就已经生命周期结束了。b_mut同理。 fn main() { let mut a = 123; let a_mut = &mut a; let b_mut = &mut a; } // 上面正确,下面报错 // error[E0499]: cannot borrow `a` as mutable more than once at a time // --> src/main.rs:4:17 // | // 3 | let a_mut = &mut a; // | ------ first mutable borrow occurs here // 4 | let b_mut = &mut a; // | ^^^^^^ second mutable borrow occurs here // 5 | println!("{}", a_mut) // | ----- first borrow later used here // 可以说是作用域重叠,也可以说是不能存在两个对a的可变引用。 fn main() { let mut a = 123; let a_mut = &mut a; let b_mut = &mut a; println!("{}", a_mut) }
  • 在有借⽤存在的情况下,不能通过原所有权型变量对值进⾏更新。当借⽤完成后(借⽤的作⽤域结束后),物归原主,⼜可以使⽤所有权型变量对值做更新操作了。
// error[E0506]: cannot assign to `a` because it is borrowed // --> src/main.rs:4:5 // | // 3 | let a_mut = &mut a; // | ------ `a` is borrowed here // 4 | a = 456; // | ^^^^^^^ `a` is assigned to here but it was already borrowed // 5 | println!("{}", a_mut) // | ----- borrow later used here fn main() { let mut a = 123; let a_mut = &mut a; a = 456; println!("{}", a_mut) } //正常,是因为a_mut在其定义的那一行就已经生命周期结束了,即借⽤完成了。 fn main() { let mut a = 123; let a_mut = &mut a; a = 456; println!("{}", a) }
  • ⼀个所有权型变量的可变引⽤也具有所有权特征,它可以被理解为那个所有权变量的独家代理,具有排它性。
  • 从下面也能看出来,虽然基础类型没有所有权,其实就是实现的copy,但是基础类型的引用类型是有所有权一说了。
// error[E0382]: borrow of moved value: `b` // --> src/main.rs:5:20 // | // 3 | let b = &mut a; // | - move occurs because `b` has type `&mut i32`, which does // not implement the `Copy` trait // 4 | let c = b; // | - value moved here // 5 | println!("{}", b) // | ^ value borrowed here after move // | fn main() { let mut a = 123; let b = &mut a; let c = b; println!("{}", b) // 改为c即正确 }