(我觉得所有权这节主要是概念,极少数才是语法,个人觉得用处不大,简洁描述,作用域就是变量在{}的范围内,就有作用,出{}会释放内存;所有权就是能否将变量本身(地址、长度、内容【原文说是容量,我觉得称呼为内容更合适】)返回,能返回就是拥有了变量的所有权;还有一些其他使用方法,感兴趣可以看一下)
所有权,让Rust无需垃圾回收,即可保证内存安全
(4.1)所有权
- Rust 中的每一个值都有一个被称为其 所有者 的变量。
- 值在任一时刻有且只有一个所有者。
- 当所有者(变量)离开作用域,这个值将被丢弃。
1.栈与堆
1.1栈:以放入值的顺序存储值并以相反顺序取出值,即后进先出,栈中所有数据都必须占用已知且固定的大小。
1.2堆:无序,当向堆放入数据时,要请求一定大小的空间,堆中数据与栈相反,不固定、大小未知
2.变量作用域:一个项在程序中有效的范围。(项所在的{}的范围内,即为它的作用域)
3.String类型:
3.1字符串字面量String:被强制编码进程序里的字符串值,它们在定义后不可变。
3.2堆上String:创建:let s = String::from("hello");(“::”是运算符,允许将特定的from函数置于String类型的命名空间下)
fn main() {
let mut s = String::from("hello");//String::from,在堆上请求内存
// 定义s为堆上String类型,内容为hello
s.push_str(", world!");
// push_str() 在字符串后追加字面值
println!("{}", s);
// 将打印 `hello, world!`
}
4.内存与分配
4.1内存
请求的内存,在作用域结束后,将被释放;
4.2(堆上)移动:移动赋值:不复制内容
fn main() {
let s1 = String::from("hello");
let s2 = s1;//s2复制s1的指针、长度、地址,同时释放s1
/*内存中,s2中复制的是s1的指针(地址)、长度、容量,而不是复制内容,
即s1、s2指向同一位置的数据*/
}
4.3(堆上)克隆:clone():克隆赋值,复制一切
fn main() {
let s1 = String::from("hello");
let s2 = s1.clone();//将s1克隆到s2
println!("s1 = {}, s2 = {}", s1, s2);
}
4.4栈上拷贝
fn main() {
let x = 5;
let y = x;
println!("x = {}, y = {}", x, y);
}
即堆上旧变量在移动赋值给其他变量后会被释放,而栈上的旧变量在赋值给其他变量后,旧变量仍可使用
5.所有权与函数:通过函数操作堆上、栈上赋值,旧变量什么时候消失,还是遵照:堆上旧变量在移动赋值给其他变量后会被释放,而栈上的旧变量在赋值给其他变量后,旧变量仍可使用。
fn main() {
let s = String::from("hello"); // s 进入作用域
takes_ownership(s); // s 的值移动到函数里 ...
// ... 所以到这里不再有效
let x = 5; // x 进入作用域
makes_copy(x); // x 应该移动函数里,
// 但 i32 是 Copy 的,所以在后面可继续使用 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 移出作用域。不会有特殊操作
6.返回值与作用域
6.1转移返回值的所有权
(下面示例和中文版不太符合,我复制过来编译,提示出错,改完后才能编译,更改了以下:s1改为_s1;s2改为_s2;就能编译了)
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 并移出给调用的函数
}
6.2返回参数的所有权
使用元组返回参数的所有权;需要将hello返回调用函数,才能使用hello。
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)
}
(4.2)引用与借用(可变引用)
- 在任意给定时间,要么 只能有一个可变引用,要么 只能有多个不可变引用。
- 引用必须总是有效的。
1.引用:只传递地址,但无所有权;调用函数,运行时,s访问s1再访问对应的内容(和c的指针类似)
fn main() {
let s1 = String::from("hello");
let len = calculate_length(&s1);
println!("The length of '{}' is {}.", s1, len);
}
fn calculate_length(s: &String) -> usize {
s.len()
}
2.可变引用:即借用:在同一时间,只能有一个对某一特定数据的可变引用
fn main() {
let mut s = String::from("hello");
change(&mut s);
}
fn change(some_string: &mut String) {
some_string.push_str(", world");
}
3.悬垂引用:自定义函数创建的变量,需返回变量,而非返回地址
fn main() {
let reference_to_nothing = dangle();
}
fn dangle() -> &String {
let s = String::from("hello");
s//自定义函数返回s,
//还是所有权问题,s在自定义函数结束后,被释放,因此需返回具体的值
}
(4.3)切片Slice类型:允许你引用集合中一段连续的元素序列,而不用引用整个集合
fn main() {
let s = String::from("hello world");//相当于数组
let hello = &s[0..5];
let world = &s[6..11];
}
字符串字面量String的类型为&str,&str不可变,所以字符串字面量类型不可变