rustdesk api 服务器 rust服务器main
// 本页是对RUST第四章的学习汇总记录。
书址
4.1. 什么是所有权
1. 所有权是RUST语言的核心功能(之一)他是一种管理内存的方式
2. **所有权规则
3. **变量作用域
fn main() { { // s 在这里无效, 它尚未声明 let s = "hello"; // 从此处起,s 是有效的(进入作用域) // 使用 s } // 此作用域已结束,s 不再有效(离开作用域) }- 当 s 进入作用域 时,它就是有效的。
- 这一直持续到它 离开作用域 为止。
4. String类型(字符串)
- 必须在运行时向内存分配器(memory allocator)请求内存。
- 需要一个当我们处理完 String 时将内存返回给分配器的方法。
5. 变量与数据交互的方式(一):移动
fn main() { let s1 = String::from("hello"); let s2 = s1; //如果这个时候再次尝试使用s1 println!("{},world",s1); //此时程序将会报错,因为Rust禁止无效的引用 }- 在 let s2=s1 之后,s1就不再有效。(Rust 不需要在 s1 离开作用域后清理任何东西。)
- 有一个特例:Copy trait(如果一个类型实现了 Copy trait,那么一个旧的变量在将其赋值给其他变量后仍然可用)可以不使用clone
- 任何不需要分配内存或某种形式资源的类型都可以实现 Copy 。如下是一些 Copy 的类型:
6. 变量与数据交互的方式(二):克隆(clone)
fn main() { let s1 = String::from("hello"); let s2 = s1.clone(); println!("s1 = {}, s2 = {}", s1, s2); }- 当出现 clone 调用时,你知道一些特定的代码被执行而且这些代码可能相当消耗资源。你很容易察觉到一些不寻常的事情正在发生。
7. 所有权与函数
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 移出作用域。没有特殊之处- 变量的所有权总是遵循相同的模式:将值赋给另一个变量时移动它。当持有堆中数据值的变量离开作用域时,其值将通过 drop 被清理掉,除非数据被移动为另一个变量所有。
- 转移返回值的所有权:
补充:我们可以使用元组来返回多个值
//元组 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. 引用与借用
引用(reference)像一个指针,因为它是一个地址,我们可以由此访问储存于该地址的属于其他变量的数据。(与指针不同,引用确保指向某个特定类型的有效值。)
规则:
- 相比较元组:变量声明和函数返回值中的所有元组代码都消失了。
- &s1 语法让我们创建一个 指向 值 s1 的引用,但是并不拥有它。因为并不拥有这个值,所以当引用停止使用时,它所指向的值也不会被丢弃。
- 我们将创建一个引用的行为称为 借用(borrowing)。
- 默认不允许修改引用的值
可变引用
fn main() { let mut s = String::from("hello"); change(&mut s); } fn change(some_string: &mut String) { some_string.push_str(", world"); }- 如果你有一个对该变量的可变引用,你就不能再创建对该变量的引用。尝试对一个变量创建两个可变引用的代码会失败(报错)一个可变引用必须持续到在一个println!(或者别的什么)中使用它。(一个引用的作用域从声明的地方开始一直持续到最后一次使用为止。)
- 这样子限制的好处是避免数据竞争(这会导致未定义行为,难以在运行时追踪,并且难以诊断和修复):
- 两个或更多指针同时访问同一数据
- 至少有一个指针被用来写入数据
- 没有同步数据访问的机制
- 也不可以在拥有不可变引用的时候拥有可变引用(不能同时使用可变和不可变引用)
悬垂引用
悬垂指针是其指向的内存可能已经被分配给其它持有者在(Rust 中,编译器确保引用永远也不会变成悬垂状态:当你拥有一些数据的引用,编译器确保数据不会在其引用之前离开作用域。)
fn main() { let reference_to_nothing = dangle(); } fn dangle() -> &String { // dangle 返回一个字符串的引用 let s = String::from("hello"); // s 是一个新字符串 &s // 返回字符串 s 的引用 } // 这里 s 离开作用域并被丢弃。其内存被释放。 // 危险!- 因为 s 是在 dangle 函数内创建的,当 dangle 的代码执行完毕后,s 将被释放。不过我们尝试返回它的引用。这意味着这个引用会指向一个无效的 String( s 已经被释放而无效化),这可不对!Rust 不会允许我们这么做。
Slice类型
slice 允许你引用集合中一段连续的元素序列,而不用引用整个集合。slice 是一类引用,所以它没有所有权。
- 编写一个函数,该函数接收一个用空格分隔单词的字符串,并返回在该字符串中找到的第一个单词。如果函数在该字符串中并未找到空格,则整个字符串就是一个单词,所以应该返回整个字符串。
- 对于 Rust 的 .. range 语法,如果想要从索引 0 开始,可以不写两个点号之前的值。换句话说,如下两个语句是相同的:
- 如果 Slice 包含 String 的最后一个字节,也可以舍弃尾部的数字。这意味着如下也是相同的:
- 也可以同时舍弃这两个值来直接获取整个字符串的Slice,所以如下亦是相同的
- 字符串字面值就是Slice
- 字符串Slice作为参数:如果有一个字符串 slice,可以直接传递它。如果有一个 String,则可以传递整个 String 的 slice 或对 String 的引用。
- 其他类型的Slice:就跟我们想要获取字符串的一部分那样,引用数组的一部分的时候,我们可以这样做:
总结:
- 所有权、借用和 slice 这些概念让 Rust 程序在编译时确保内存安全。Rust 语言提供了跟其他系统编程语言相同的方式来控制你使用的内存,但拥有数据所有者在离开作用域后自动清除其数据的功能意味着你无须额外编写和调试相关的控制代码。