所有权
所有权是Rust独有的特性,它让Rust无需GC就可以保证内存安全。
所有权解决的问题
Stack-栈
压入(把值压入stack不叫分配)
stack存放的是固定大小的数据。
应为指针是已知固定大小的,可以把指针存放到stack上。
把数据压入stack比在heap上分配快的多。
Heap-堆
分配
跟踪代码那些部分正在使用heap的那些数据。
最小化heap上的重复数据。
清理heap上未使用的数据,以免空间不足。
管理heap数据才是所有权存
在的特性。
所有权规则
- 每一个值都有一个变量,这个变量是该值的所有者。
- 每个值同时只能有一个所有者(变量)。
- 当所有者(变量)超出作用域(scope)时,该值将被删除(drop)。
作用域
fn main() {
{ // s 在这里无效,它尚未声明
let s = "hello"; // 从此处起,s 是有效的
// 使用 s
println!("s is {}", s);
} // 此作用域已结束,s不再有效
}
变量和数据交换方式
移动
基本数据类型
stateDiagram Stack栈 state Stack栈 { [*] --> y=5 : <font color=#32CD32>new</font> y=5 --> x=5 x=5 --> [*] }x、y已知固定大小的简单值,都会被压到stack中
let x = 5;
let y = x;
复杂数据类型
Alt text
s2只复制了s1在stack上的指针信息,并没有复制heap上数据。
并且上s1废弃,当s1离开作用域时不需要释放任何东西。
如果s1没有被废弃,那么当s1,s2离开作用域时,会释放两次heap内容,内存不安全。
stateDiagram direction LR Stack栈 Heap堆 s1变量 --> hello字符串 : <font color=FF0000>1.失去所有权</font> s2变量 --> hello字符串 : <font color=#32CD32>2.获取所有权</font> state Stack栈{ s1变量 s2变量 } state Heap堆{ hello字符串 }浅拷贝和深拷贝,Rus复制了指针、长度、容量视为浅拷贝,但是会把原先的变量失效了,所以叫做
移动
。
fn main() {
let s1 = String::from("hello");
let s2 = s1; // 所有权发生了转移,s1不再有效
// println!("s1 = {}", s1) // 如果接着打印会报错
println!("s2 = {}", s2);
}
克隆
stateDiagram-v2 direction LR Stack栈 Heap堆 s1变量 --> hello字符串 s2变量 --> hello字符串' : <font color=#32CD32>复制s1在heap的内容</font> state Stack栈{ s1变量 s2变量 } state Heap堆{ hello字符串 hello字符串' }2s把s1的heap上的数据clone了一份
fn main() {
let s1 = String::from("hello");
let s2 = s1.clone();
println!("s1 = {}", s1);
println!("s2 = {}", s2);
}
拷贝
1.Copy trait通常用于像整型等存储在栈上的类型。
2.如果一个类型实现了Copy trait,在赋值给其他变量后,旧的变量仍然可用,即发生按位复制而不是所有权转移。
3.一个类型实现了Drop trait, 那么不允许实现Copy trait,因为这类类型在值离开作用域时需要特殊处理。
以下类型实现了 Copy trait
- 所有整数类型,比如 u32。
- 布尔类型,bool,它的值是 true 和 false。
- 所有浮点数类型,比如 f64。
- 字符类型,char。
- 元组,当且仅当其包含的类型也都实现 Copy 的时候。比如,(i32, i32) 实现了 Copy,但 (i32, String) 就没有。
fn main() {
let x = 5;
let y = x;
println!("x = {}, y = {}", x, y);
}
所有权与函数
fn main() {
let s = String::from("hello"); // s 进入作用域
takes_ownership(s); // s 的值移动到函数里 ...
// println!("s: {}", s); 所以到这里s就失效了,如果打印的话就报错了
let x = 5; // x 进入作用域
makes_copy(x); // x 应该移动函数里,
// 但 i32 是 Copy 的,所以在后面可继续使用 x
println!("x: {}", x);
} // 这里, x 先移出了作用域,然后是 s。但因为 s 的值已被移走,所以不会有特殊操作
fn takes_ownership(some_string: String) { // some_string 进入作用域
println!("{}", some_string);
} // 这里,some_string 移出作用域并调用 `drop` 方法。占用的内存被释放
fn makes_copy(some_integer: i32) { // some_integer 进入作用域
println!("{}", some_integer);
} // 这里,some_integer 移出作用域。不会有特殊操作
返回值与作用域
fn main() {
let s1 = gives_ownership(); // gives_ownership 将返回值
// 移给 s1
let s2 = String::from("hello"); // s2 进入作用域
let s3 = takes_and_gives_back(s2); // s2 被移动到
// takes_and_gives_back 中,
// 它也将返回值移给 s3
} // 这里, s3 移出作用域并被丢弃。s2 也移出作用域,但已被移走,
// 所以什么也不会发生。s1 移出作用域并被丢弃
fn gives_ownership() -> String { // gives_ownership 将返回值移动给
// 调用它的函数
let some_string = String::from("yours"); // some_string 进入作用域
some_string // 返回 some_string 并移出给调用的函数
}
// takes_and_gives_back 将传入字符串并返回该值
fn takes_and_gives_back(a_string: String) -> String { // a_string 进入作用域
a_string // 返回 a_string 并移出给调用的函数
}
转移返回值的所有权
在每一个函数中都获取所有权并接着返回所有权有些啰嗦。
所以Rust
对此提供了一个功能,叫做引用
fn main() {
let s1 = String::from("hello");
let (s2, len) = calculate_length(s1);
println!("The length of '{}' is {}.", s2, len);
}
fn calculate_length(s: String) -> (String, usize) {
let length = s.len(); // len() 返回字符串的长度
// 每次都需要返回所有权,麻烦
(s, length)
}
标签:String,作用域,s2,s1,let,所有权,hello
From: https://www.cnblogs.com/lxd670/p/18411892