Rust 所有权(Ownership)

2023-10-28 15:20
文章标签 rust ownership 所有权

本文主要是介绍Rust 所有权(Ownership),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、Ownership是什么?
  • 二、规则集
  • 三、理论+示例
  • 四、引用和借用
  • 五、切片类型(slice)
  • 总结


前言

Rust的所有权概念是其最独特的特性之一,它对Rust的其它部分也有很大影响。所有权概念让Rust在没有GC的情况下,也能够保证内存安全(无需手动管理内存CC++)。

GC就是garbage collection,比较直观,JavaGo等均需要。
内存安全广义讲应该包括没有野指针、内存重复释放等错误。进一步的,在rust中结合其它特性可以实现多线程的并发安全访问,CC++常见的内存泄露则不属于不安全(虽然也是问题)。


一、Ownership是什么?

概括的讲,所有权是rust管理内存的一个规则集。
众所周知,计算机程序都需要管理内存,只是不同的编程语言采用了不同的方法。主要分两类:

  • 不断检测未使用内存并释放的GC(Java/go…)
  • 程序员手工管理内存(c/c++…)

而Rust采用第三种方法:通过一个包含规则集的所有权系统来管理内存。编译器会检查程序的内存使用是否符合规则集中的规则,违反规则的程序将无法编译通过。另外,这些规则的校验不会带来执行期的开销(编译期检查,编译很慢)。

规则有没有存在漏洞的可能性?导致内存不安全呢?目前看还没有

虽然对很多人来讲,所有权是一个新概念,需要时间来适应。但是,理解所有权是理解Rust独特性的基础。

二、规则集

所有权的规则集主要包括三条,可以类比C++中的RAIIunique_ptr等概念。

  • 每个值都有一个称之为owner的变量(有点类似C++ new完必须记得delete)
  • 任一时刻,每个value只有一个owner(即不可共享,实际多线程是可以共享的,不过需要用到unsafe等特性)
  • 程序执行出了owner作用域,对应的值会被丢弃(类似C++自动调用析构函数)

三、理论+示例

a. 简单情况

    {let s = String::from("hello"); // s is valid from this point forward// do stuff with s}                                  // this scope is now over, and s is no longer valid

以上为例,当s的作用域结束之时,即可以将s拥有的值所占用的堆(heap)空间释放。这个机制与C++的RAII( Resource Acquisition Is Initialization)类似,Rust通过调用drop,而C++调用析构函数。

b. 复杂情况

    let s1 = String::from("hello");let s2 = s1;println!("{}, world!", s1);

对于涉及到堆内存的对象,Rust默认采用浅复制。此外,s2 = s1执行之后,在rust中,s1将不再指向任何String对象,s2单独拥有字符串hello。图中的情况,其实更符合C++中的shared_ptr赋值后(引用计数增加)。
浅复制

解决了问题,和C++中的unique_ptr类似,shared_ptr的需求通过RcArc等特性解决。

深复制需要显式声明:

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

上述s1.clone()中的clone方法即是深拷贝,当然前提和C++一样,你得正确实现相应的函数,rust 中就是Clone trait。
深复制

Copy trait? 有什么意义吗?因为所有类型的对象都可以copy吧?

函数调用的实参,函数返回值,均会转移对象的所有权。

四、引用和借用

引用并没有对应值的所有权,因此不会触发调用drop。引用可以成为借用。

多个变量指向同一个对象,没有共享问题?单线程下不会,多线程下则不允许。

同一个对象不能同时被借用多次,下面代码编译错误。

fn main() {let mut s = String::from("hello");let r1 = &mut s;let r2 = &mut s;println!("{}, {}", r1, r2);
}

这个限制可以在编译期消除数据竞争。

但是,对象已经可以被两个变量修改了啊?可变引用变量+原来的变量,这两者的竞争如何解决呢,多线程不允许共享,如果需要共享,则需要用其它机制。

不可变引用可以有多个,但是可变引用只能有一个。当不可变引用完全无用之前,不能有新的可变引用。(所谓的共享不可变,可变不共享

编译期检测虚悬引用(dangling)
引用规则:

  • 任一时刻,只能由一个可变引用、或者若干不可变引用
  • 引用必须一直有效

五、切片类型(slice)

避免下面这种问题,s.clear()之后,s[word]访问其实已经非法,运行期报错。

thread ‘main’ panicked at ‘byte index 5 is out of bounds of ``’, src/main.rs:20:18

fn first_word(s: &String) -> usize {let bytes = s.as_bytes();for (i, &item) in bytes.iter().enumerate() {if item == b' ' {return i;}}s.len()
}fn main() {let mut s = String::from("hello world");let word = first_word(&s); // word will get the value 5s.clear(); // this empties the String, making it equal to ""// word still has the value 5 here, but there's no more string that// we could meaningfully use the value 5 with. word is now totally invalid!
}

总结

初看下来,Rust实现内存安全的机制,并没有什么高深的。其实就是可变不共享,共享不可变

这篇关于Rust 所有权(Ownership)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/294137

相关文章

Rust中的注释使用解读

《Rust中的注释使用解读》本文介绍了Rust中的行注释、块注释和文档注释的使用方法,通过示例展示了如何在实际代码中应用这些注释,以提高代码的可读性和可维护性... 目录Rust 中的注释使用指南1. 行注释示例:行注释2. 块注释示例:块注释3. 文档注释示例:文档注释4. 综合示例总结Rust 中的注释

Rust格式化输出方式总结

《Rust格式化输出方式总结》Rust提供了强大的格式化输出功能,通过std::fmt模块和相关的宏来实现,主要的输出宏包括println!和format!,它们支持多种格式化占位符,如{}、{:?}... 目录Rust格式化输出方式基本的格式化输出格式化占位符Format 特性总结Rust格式化输出方式

Rust中的Drop特性之解读自动化资源清理的魔法

《Rust中的Drop特性之解读自动化资源清理的魔法》Rust通过Drop特性实现了自动清理机制,确保资源在对象超出作用域时自动释放,避免了手动管理资源时可能出现的内存泄漏或双重释放问题,智能指针如B... 目录自动清理机制:Rust 的析构函数提前释放资源:std::mem::drop android的妙

Rust中的BoxT之堆上的数据与递归类型详解

《Rust中的BoxT之堆上的数据与递归类型详解》本文介绍了Rust中的BoxT类型,包括其在堆与栈之间的内存分配,性能优势,以及如何利用BoxT来实现递归类型和处理大小未知类型,通过BoxT,Rus... 目录1. Box<T> 的基础知识1.1 堆与栈的分工1.2 性能优势2.1 递归类型的问题2.2

在Rust中要用Struct和Enum组织数据的原因解析

《在Rust中要用Struct和Enum组织数据的原因解析》在Rust中,Struct和Enum是组织数据的核心工具,Struct用于将相关字段封装为单一实体,便于管理和扩展,Enum用于明确定义所有... 目录为什么在Rust中要用Struct和Enum组织数据?一、使用struct组织数据:将相关字段绑

浅析Rust多线程中如何安全的使用变量

《浅析Rust多线程中如何安全的使用变量》这篇文章主要为大家详细介绍了Rust如何在线程的闭包中安全的使用变量,包括共享变量和修改变量,文中的示例代码讲解详细,有需要的小伙伴可以参考下... 目录1. 向线程传递变量2. 多线程共享变量引用3. 多线程中修改变量4. 总结在Rust语言中,一个既引人入胜又可

Rust 数据类型详解

《Rust数据类型详解》本文介绍了Rust编程语言中的标量类型和复合类型,标量类型包括整数、浮点数、布尔和字符,而复合类型则包括元组和数组,标量类型用于表示单个值,具有不同的表示和范围,本文介绍的非... 目录一、标量类型(Scalar Types)1. 整数类型(Integer Types)1.1 整数字

Rust中的Option枚举快速入门教程

《Rust中的Option枚举快速入门教程》Rust中的Option枚举用于表示可能不存在的值,提供了多种方法来处理这些值,避免了空指针异常,文章介绍了Option的定义、常见方法、使用场景以及注意事... 目录引言Option介绍Option的常见方法Option使用场景场景一:函数返回可能不存在的值场景

【Rust练习】12.枚举

练习题来自:https://practice-zh.course.rs/compound-types/enum.html 1 // 修复错误enum Number {Zero,One,Two,}enum Number1 {Zero = 0,One,Two,}// C语言风格的枚举定义enum Number2 {Zero = 0.0,One = 1.0,Two = 2.0,}fn m

linux中使用rust语言在不同进程之间通信

第一种:使用mmap映射相同文件 fn main() {let pid = std::process::id();println!(