RCore – Rustlings 知识点整理(一):所有权的转移
RCore – Rustlings 知识点整理(一):所有权的转移

RCore – Rustlings 知识点整理(一):所有权的转移

一、 所有权系统的主要规则

Rust所有权系统的主要规则有:

  • Rust中每一个值都有一个变量关联,这个变量称作owner
  • 一个值某一时刻只能有一个owner
  • 当owner的作用域结束,对应值的生命周期也结束,会即时被销毁

二、 Move 与 Clone

对于不同变量而言,所有权对相同内存地址的值存在两种不同的交互方式,Move 与 Clone。

1. Move

以字符串为例:

let s1 = String::from("hello");
let s2 = s1;

从 s1 到 s2,并不会拷贝 s1 中的内容,然后新赋值给 s2 ,因为这样会导致开辟内存空间时花费大量时间。字符串中的内容存储在堆上,然后使用一个指针指向堆中的地址。

再而言,同样也不会复制一个 s1 的指针地址给 s2 ,因为如果 s1 与 s2 有着相同的地址空间,那么在变量离开作用域时,他们会调用 Drop 函数,释放地址空间,但是因为他们有着相同的地址,会导致二次释放问题。两次释放会导致内存损坏,可能会导致安全漏洞。

为了保证内存安全,在 let s2 = s1 的赋值完成后,Rust 认为 s1 已经是无效的。因此 Rust 在 s1 离开作用域后,不再需要释放操作(drop)。可以知道,后续对 s1 的访问都会是非法的。这是区别于大多数变成语言的。

这类似移动了 s1 的地址指针给 s2 的操作就称为 Move 。

2. Clone

对于类似深拷贝的操作,Rust 提供了 clone 方法来实现,如下:

let s1 = String::from("hello");
let s2 = s1.clone();

println!("s1 = {}, s2 = {}", s1, s2);

3. Copy Trait

在实际使用过程中,可能会发现像下面这种类似的现象:

    let x = 5;
    let y = x;
    println!("x = {}, y = {}", x, y);

虽然没有调用 clone 方法,但是所有权并没有转移。看起来像是执行了复制一样。

Rust 这样做的原因是这两个整型都完全存储在栈上,所以我们完全可以快速复制其值,没有必要让其中一个变量失效。

Rust 中有一个Copy trait的特殊注解,可以用在类似整型这样的存储在栈上的类型上。

如果一个类型实现了 Copy trait ,那么一个变量在赋值给另一个同类型变量后,将仍然有效。

但是如果一个类型实现了Drop trait,则Rust不允许用Copy trait来注解一个类型,这将产生编译错误。即 Drop trait 与 Copy trait 不可共存。

那么哪些类型实现了 Copy trait 呢?你可以查看给定类型的文档来确认,不过作为一个通用的规则,任何一组简单标量值的组合都可以实现 Copy,任何不需要分配内存或某种形式资源的类型都可以实现 Copy 。如下是一些 Copy 的类型:

  • 所有整数类型,比如 u32。
  • 布尔类型,bool,它的值是 true 和 false。
  • 所有浮点数类型,比如 f64。
  • 字符类型,char。
  • 元组,当且仅当其包含的类型也都实现 Copy 的时候。比如,(i32, i32) 实现了 Copy,但 (i32, String) 就没有。

三、 引用与借用

引用(reference)像一个指针,因为它是一个地址,我们可以由此访问储存于该地址的属于其他变量的数据。 与指针不同,引用确保指向某个特定类型的有效值。

下面是如何定义并使用一个(新的)calculate_length 函数,它以一个对象的引用作为参数而不是获取值的所有权:

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()
}

&s1 语法让我们创建一个指向值 s1 的引用,但是并不拥有它。因为并不拥有这个值,所以当引用停止使用时,它所指向的值也不会被丢弃。

我们将创建一个引用的行为称为借用(borrowing)。正如现实生活中,如果一个人拥有某样东西,你可以从他那里借来。当你使用完毕,必须还回去。我们并不拥有它。

但是对于普通的引用,我们不能修改其地址的值。

可变引用

在 & 的基础上加一个 mut,例子如下:

fn main() {
    let mut s = String::from("hello");
    change(&mut s);
}

fn change(some_string: &mut String) {
    some_string.push_str(", world");
}

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注