Rust 所有权机制原理深入剖析

 更新时间:2023年01月03日 14:01:52   作者:ooooooh灰灰  
这篇文章主要为大家介绍了Rust 所有权机制原理深入剖析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

what's ownership?

常见的高级语言都有自己的 Garbage Collection(GC)机制来管理程序运行的内存,例如 Java、Go 等。而 Rust 引入了一种全新的内存管理机制,就是 ownership(所有权)。它在编译时就能够保证内存安全,而不需要 GC 来进行运行时的内存回收。

在 Rust 中 ownership 有以下几个规则:

  • 每个值都有一个 woner(所有者)
  • 在同一时间,每个值只能有一个 owner
  • 当 owner 离开作用域,这个值就会被丢弃

Scope (作用域)

通过作用域来划分 owner 的生命周期,作用域是一段代码的范围,例如函数体、代码块、if 语句等。当 owner 离开作用域,这个值就会被丢弃

example:

fn main() {
    let s = String::from("hello"); // 变量 s 进入作用域,分配内存
    // s 在这里可用
} // 函数体结束,变量 s 离开作用域,s 被丢弃,内存被回收

ownership transfer(所有权转移)

和大多数语言一样,Rust 在栈上分配基本类型的值,例如整型、浮点型、布尔型等。而在堆上分配复杂类型的值,例如 String、Vec 等。所以,这里就引入了两个概念,moveclone

move

move 操作会将变量的所有权转移给另一个变量,这样原来的变量就不能再使用了。这里需要注意的是,move 操作只会发生在栈上的值,因为在堆上的值是不可复制的,所以只能通过 clone 操作来复制。

example:

fn main(){
    let s1 = String::from("hello");
    let s2 = s1;
    print!("s1 = {}, s2 = {}", s1, s2);
}

在上面的代码例子中,如果你执行就会在编译时报错:

  --> src/main.rs:11:32
   |
9  |     let s1 = String::from("hello");
   |         -- move occurs because `s1` has type `String`, which does not implement the `Copy` trait
10 |     let s2 = s1;
   |              -- value moved here
11 |     print!("s1 = {}, s2 = {}", s1, s2);
   |                                ^^ value borrowed here after move

编译器提示我们,s1 在赋值给 s2 时发生了 move 的操作,它把字符串 hello 的所有权移交给了 s2,此时 s1 的作用域到这里就结束了,所以后面再使用 s1 就会报错。

clone

clone 操作会将变量的值复制一份,这样原来的变量和新的变量就都可以使用了。

这里需要注意的是,clone 操作只会发生在堆上的值,因为在栈上的值是可复制的,所以只能通过 move 操作来转移所有权。

example:

fn main(){
    let s1 = String::from("hello");
    let s2 = s1.clone();
    print!("s1 = {}, s2 = {}", s1, s2);
}

我们对 s1 进行 clone 操作,这样 s1s2 都可以使用了,而且 s1 的所有权也没有被转移,所以后面还可以继续使用 s1

copy

如果一个类型实现了 copy 这个 trait,使用它的变量不会移动,而是被简单地复制,使它们在分配给另一个变量后仍然有效。

example:

fn main() {
    let x = 5;
    let y = x;
    print!("x = {}, y = {}", x, y);
}

x 赋值给 y 后,xy 都可以使用,而且 x 的所有权也没有被转移,所以后面还可以继续使用 x。这是因为 i32 这个类型实现了 copy 这个 trait,所以 x 的值被复制了一份,所以 xy 都可以使用。

以下这些数据类型实现了 copy 这个 trait:

  • 所有的整数类型,例如:u32i32
  • 布尔类型,bool,有 truefalse 两个值。
  • 所有的浮点数类型,例如:f64f32
  • 字符类型,char
  • 元组,当且仅当它们的元素类型都实现了 copy 这个 trait。例如,(i32, i32) 实现了 copy,但是 (i32, String) 就没有实现。

References and Borrowing(引用和借用)

我们将创建引用的动作称为借用。就像在现实生活中一样,如果一个人拥有某样东西,你可以向他们借用。完成后,您必须将其归还。你不拥有它。 引用有以下几个规则:

  • 在任何给定时间,你可以拥有任意数量的引用,但是只能拥有一个可变引用。
  • 引用必须总是有效的。

example1:

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()
} // s 作用域失效,但是由于 s 是一个引用,没有所有权,所以不会发生任何事情

上面代码中,我们使用符号 & 来创造一个变量的引用。这里我们使用 &s1 来把这个引用指向 s1。函数 calculate_length 的参数 s 的类型是 &String,这意味着它是一个指向 String 类型的引用,然后在函数体内获取 s 的长度并返回给调用者。

example2:

fn main(){
    // 同一时间可以拥有多个不可变引用
    let s1 = String::from("hello");
    let s2 = &s1;
    let s3 = &s1;
    println!("s1 = {}, s2 = {}, s3 = {}", s1, s2, s3);
}

Mutable References(可变引用)

可变引用指的是可以改变引用值的引用。在同一作用域中,同一时间只能有一个可变引用。

example:

fn main(){
    let mut s = String::from("hello");
    change(&mut s);
    println!("{}", s);
}
fn change(some_string: &mut String) {
    some_string.push_str(", world");
}

上面代码中,我们用 mut 先创建了一个可变变量 s,然后使用 &mut s 创建了一个指向 s 的可变引用。函数 change 的入参也是一个指向 String 类型的可变引用,这样我们就可以在函数 change 中改变 s 的值了。

example2:

fn main() {
    let mut s = String::from("hello");
    let r1 = &mut s;
    let r2 = &mut s;  // 在这里。编译器会报错,因为在同一作用域中,同一时间只能有一个可变引用。
    println!("{}, {}", r1, r2);
}
  --> src/main.rs:41:14
   |
40 |     let r1 = &mut s;
   |              ------ first mutable borrow occurs here
41 |     let r2 = &mut s;
   |              ^^^^^^ second mutable borrow occurs here
42 |
43 |     println!("{}, {}", r1, r2);
   |                        -- first borrow later used here

Dangling References(悬垂引用)

悬垂引用是指引用一个不存在的值。在 Rust 中,这是不可能的,因为编译器会在编译时就检查这种情况。下面是一个例子:

fn main() {
    let reference_to_nothing = dangle(); // 获得一个指向不存在值的引用
}
fn dangle() -> &String {
    let s = String::from("hello"); // s 进入作用域
    &s // 返回 s 的引用
} // s 作用域结束,s 被丢弃,内存被释放
  --> src/main.rs:51:16
   |
51 | fn dangle() -> &String {
   |                ^ expected named lifetime parameter

因为变量 s 的作用域只在 dangle 函数内,当 dangle 函数返回 s 的引用时,s 已经被释放了,所以这个引用就是悬垂引用了。 解决这个的方法是返回一个 String 而不是一个引用,这样 s 就不会被释放,而是把 s 的所有权转移给了调用者,也就不存在悬垂引用了。

fn dangle() -> String {
    let s = String::from("hello");
    s
}

以上就是Rust 所有权机制原理深入剖析的详细内容,更多关于Rust 所有权机制的资料请关注脚本之家其它相关文章!

相关文章

  • rust中trait的使用方法详解

    rust中trait的使用方法详解

    trait用中文来讲就是特征,它就是一个标记,只不过这个标记被用在特定的地方,也就是类型参数的后面,下面我们就来学习一下trait的具体使用方法吧
    2023-12-12
  • Rust控制流运算符match的用法详解

    Rust控制流运算符match的用法详解

    match 是Rust中一个极为强大的控制流运算符,用于模式匹配和控制流的选择,它允许将一个值与一系列的模式相比较,根据匹配的模式执行相应代码,本文给大家详细介绍了Rust控制流运算符match的用法,需要的朋友可以参考下
    2024-01-01
  • Rust Atomics and Locks并发基础理解

    Rust Atomics and Locks并发基础理解

    这篇文章主要为大家介绍了Rust Atomics and Locks并发基础理解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-02-02
  • Rust常用功能实例代码汇总

    Rust常用功能实例代码汇总

    通过一系列实用示例展示了Rust在文件操作、网络请求、并发编程、命令行工具以及数据库操作等方面的应用,这些示例不仅展示了 Rust 的强大功能,还提供了实际开发中的指导和参考,通过这些示例,您可以更好地理解 Rust 的特性,并将其应用于您的项目中
    2024-12-12
  • 关于Rust 使用 dotenv 来设置环境变量的问题

    关于Rust 使用 dotenv 来设置环境变量的问题

    在项目中,我们通常需要设置一些环境变量,用来保存一些凭证或其它数据,这时我们可以使用dotenv这个crate,接下来通过本文给大家介绍Rust 使用dotenv来设置环境变量的问题,感兴趣的朋友一起看看吧
    2022-01-01
  • 一文带你了解Rust是如何处理错误的

    一文带你了解Rust是如何处理错误的

    程序在运行的过程中,总是会不可避免地产生错误,而如何优雅地解决错误,也是语言的设计哲学之一。本文就来和大家来了Rust是如何处理错误的,感兴趣的可以了解一下
    2022-11-11
  • rust的nutyp验证和validator验证数据的方法示例详解

    rust的nutyp验证和validator验证数据的方法示例详解

    本文介绍了在Rust语言中,如何使用nuType和validator两种工具来对Cargo.toml和modules.rs文件进行验证,通过具体的代码示例和操作步骤,详细解释了验证过程和相关配置,帮助读者更好地理解和掌握使用这两种验证工具的方法,更多Rust相关技术资讯,可继续关注脚本之家
    2024-09-09
  • Rust语言从入门到精通系列之Iterator迭代器深入详解

    Rust语言从入门到精通系列之Iterator迭代器深入详解

    这篇文章主要为大家介绍了Rust语言从入门到精通系列之Iterator迭代器深入详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-04-04
  • 使用Rust开发小游戏完成过程

    使用Rust开发小游戏完成过程

    这篇文章主要介绍了使用Rust开发小游戏的完整过程,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2023-11-11
  • Rust使用Trait对象实现多态的详细步骤

    Rust使用Trait对象实现多态的详细步骤

    本文详细介绍了在Rust中使用Trait对象实现运行时多态的方法,通过一个图形渲染系统的案例展示了如何统一管理不同类型的图形对象,文章涵盖了Trait对象的核心概念、语法、性能考量以及在实际项目中的应用场景,感兴趣的朋友跟随小编一起看看吧
    2025-11-11

最新评论