新手眼中的rust所有权规则(代码片段)

author author     2022-12-12     218

关键词:

新手眼中的 Rust 所有权规则

如果你有关注本人博客,那么很明显,从今年年初开始,我便开始学习 Rust。此文与之前风格略有不同,旨在总结阅读 Rust 书籍时遇到的要点。到目前为止,它包含了我对 Rust 所有权规则的所有理解。

Rust 的主要亮点之一是它提供了内存安全性。通过提供编译时保证,将可能会导致内存错误的标志代码标记为编译时错误来做到这一点。编译时保证是通过所有权规则来实现。在这篇文章中,我总结了 Rust 所有权规则关键点,简述如下:

  • 值归变量所有。
  • 当变量超出使用范围时,变量值所占用的内存将被释放。
  • 变量值可以由其他变量使用,但是需遵守由编译器强制执行的若干规则。

变量的值可以被其他变量使用有 4 种方法,需要遵循的规则如下:

  • 克隆(clone):此处将值复制到新的变量。新变量拥有新的复制值的所有权,而原始变量保留其原始值的所有权。
  • 移动(move):所有权被转移到另一个要使用该值的变量,原始变量不再拥有所有权。
  • 不可变借用(immutable borrow)。这里没有发生所有权转移,但是可以通过另一个变量访问该值以进行读取。如果借用变量超出范围,内存不会被回收,因为借用变量没有所有权。
  • 可变借用(mutable borrow)。可以通过另一个变量对该值进行读取和写入操作。如果借用变量超出范围,内存也不会回收,因为借用变量没有所有权。

为了确保上述所有用法都不会造成内存错误,Rust 在编译时强制执行其所有权规则,概述如下:

克隆(clone)所有权规则

对值进行克隆没有什么特别,如果你已经编程了一段时间,并且知道如何传递数字和字符串之类的变量,则几乎与你习惯的机制相同。因此,实际上没有多少 Rust 特定的规则可以在这里强调。大多数情况下,你不需要克隆值,因为成本比较高。变量的其他用法与 Rust 特定的规则一起提供,因此我们将对其进行详细介绍。

移动(move)所有权规则

随着所有权 move,保存该值的原始变量将不再可用。现在只能通过新的变量访问该值。尝试使用已 move 所有权的变量,将导致编译错误:

fn main() 
    let mut original_owner = format!("Hello world");

    // move occurs to new owner    
    let new_owner = original_owner;

    // attempt to use original_owner will 
    // lead to compile time error    
    println!("", original_owner)

error[E0382]: borrow of moved value: `original_owner`

不可变借用(immutable borrow)所有权规则

使用不可变借用,借用的变量可以读取该值,但不能修改(即使原始值是 mutable 类型)不可变借用保证借用变量的值不会修改。任何试图违反这些条件的代码都将导致编译错误。

由于尝试修改不可变借用导致的编译错误


fn main() 

  let mut original_owner = format!("Hello world");
  // immutable borrow occurred
  let borrow_owner = &original_owner;

  // multiple reads possibe via owner
  println!("", original_owner);
  println!("", original_owner);

  // multiple reads possible via borrower
  println!("", borrow_owner);
  println!("", borrow_owner);
  //error: mutating not possible via borrower
  borrow_owner.push(‘.‘)


error[E0596]: cannot borrow `*borrow_owner` as mutable, as it is behind a `&` reference
  --> src/main.rs:14:5

违反不可变借用约束,修改借用的变量值而导致的编译错误

如果违反不可变借用变量不能修改的约束,Rust 编译器会在编译时报错。

fn main() 

  let mut original_owner = format!("Hello world");
  // immutable borrow occurred
  let borrow_owner = &original_owner;

  // multiple reads possible via owner
  println!("", original_owner);
  println!("", original_owner);

  // multiple reads possible via borrower
  println!("", borrow_owner);

  // original owner trying to mutate
  original_owner.push(‘.‘);

  println!("", borrow_owner);


error[E0502]: cannot borrow `original_owner` as mutable because it is also borrowed as immutable

如果将变量修改移到借用方超出范围的位置,则该修改不会导致编译错误。这是因为不存在违反对不可变借用的担保(即值不会改变)。例如以下编译:


fn main() 

  let mut original_owner = format!("Hello world");
  // immutable borrow occurred
  let borrow_owner = &original_owner;

  // multiple reads possible via owner
  println!("", original_owner);
  println!("", original_owner);

  // multiple reads possible via borrower
  println!("", borrow_owner);
  println!("", borrow_owner);

  // original owner trying to mutate
  original_owner.push(‘.‘);

可变借用(mutable borrow)的所有权规则

使用可变借用,借用方将获得该变量的所有权,这意味着新的读写必须经过借用方。具有可变所有权的原始变量也将无法读取或写入,直到可变借用方超出使用范围。该限制强制执行内存一致性。由于它有效地不允许通过除变量之外的其他方式进行任何其他写入或读取,因此它可以避免出现数据数据争用之类的情况。

可变借用的规则还确保始终只有一个活动的可变借用。这样做的道理在于,一旦你拥有多个可变借用并且具有修改的能力,就无法保持数据一致性。这些规则在编译时进行检查。

多次使用可变借用导致编译错误

fn main() 

  let mut original_owner = format!("Hello world");
  // mutable borrow occurred    
  let borrow_mutable_owner = &mut original_owner;
  // compile error due to second mutable borrow     
  let illegal_borrow = &mut original_owner; 
  println!("", borrow_mutable_owner);


error[E0499]: cannot borrow `original_owner` as mutable more than once at a time

下面是违反了可变借用独占访问的编译错误

fn main() 
    let mut original_owner = format!("Hello world");
    // mutable borrow occurred
    let borrow_mutable_owner = &mut original_owner;

    // borrowing owner can also mutate
    borrow_mutable_owner.push(‘!‘);
    // compile error due to: 
    //original owner can no longer read
    println!("", original_owner);

    // compile error due to:
    // original owner also cannot write
    original_owner.push(‘.‘);
    println!("", original_owner);
    println!("", borrow_mutable_owner);
    println!("", borrow_mutable_owner);


error[E0502]: cannot borrow `original_owner` as immutable because it is also borrowed as mutable

如果将读取及修改操作移动到可变借用变量超出范围之后,则不会有编译错误。这很好,因为不再需要执行可变借用所要求的互斥。

fn main() 

 let mut original_owner = format!("Hello world");
  // mutable borrow occurred
  let borrow_mutable_owner = &mut original_owner;

  // borrowing owner can also mutate
  // below is the last usage
  borrow_mutable_owner.push(‘!‘);

  println!("", borrow_mutable_owner);
  // borrow_mutable_owner is now out of scope
  // original owner can now read
  println!("", original_owner);

  // original owner can now write
  original_owner.push(‘.‘);
  println!("", original_owner);

编译以上代码片段,将不会发生任何错误。

小结

简洁地总结一下:

  • 使用 clone:
  • 没有什么合适规则防止内存 bug
  • 对于非常规数据结构,通常代价昂贵
  • 使用 move:
  • 将所有权从一个变量中移出后,该变量将无法再访问其最初拥有的值。
  • 使用不可变借用 (immutable borrow):
  • 可以创建无限的不可变借用
  • 所有不可变借用只能读
  • 原始拥有变量在修改其拥有的值方面存在限制,只有不存在不可变借用,它才可以修改。这样可以确保 Rust 保证对不可变借用的担保不会改变。
  • 基本上:许多读操作,没有写操作(条件是一直有读操作,否则就可以写)
  • 使用可变借用(mutable borrow):
  • 只能使用一次可变借用。
  • 所有的读写操作都只能通过活动的可变借用(active borrow)完成。
  • 只要有活动的可变借用,原始拥有变量也将无法再读取或写入。
  • 基本上:如果只有一个读和写:使用可变借用

原文:
https://www.geekabyte.io/2020/02/rust-ownership-rules.html

参考阅读:

  • Rust Web框架怎么选?研究本文就够了!
  • 深入浅出Rust异步编程之Tokio
  • Gateway技术革命 - Tengine开源Dubbo功能
  • Joe Armstrong最喜欢的一段Erlang程序
  • Clojure 语言在 2020 年的现状

本文作者 dade,由高可用架构翻译。技术原创及架构实践文章,欢迎通过公众号菜单「联系我们」进行投稿。

高可用架构

改变互联网的构建方式

技术图片

rust语言圣经10-所有权(代码片段)

...的Rust学习社区官网:https://college.rsQQ群:1009730433所有权所有的程序都必须和计算机内存打交道,如何从内存中申请空间来存放程序的运行内容,如何在不需要的时候释放这些空间,成了重中之重,也是所... 查看详情

揭秘前端眼中的rust!

导语 | 本文推选自腾讯云开发者社区-【技思广益·腾讯技术人原创集】专栏。该专栏是腾讯云开发者社区为腾讯技术人与广泛开发者打造的分享交流窗口。栏目邀约腾讯技术人分享原创的技术积淀,与广泛开发者互启迪共... 查看详情

揭秘前端眼中的rust!

导语 | 本文推选自腾讯云开发者社区-【技思广益·腾讯技术人原创集】专栏。该专栏是腾讯云开发者社区为腾讯技术人与广泛开发者打造的分享交流窗口。栏目邀约腾讯技术人分享原创的技术积淀,与广泛开发者互启迪共... 查看详情

我的第一篇rust博客(代码片段)

...rust的内存安全和线程安全实现机制就是独占内存资源的所有权,任何时候只允许只有一个变量(可以是变量的引用)对内存进行修改,并且引用是有生命周期的,编译器会约束引用的使用,它在原始变量释放内存之前必须归还... 查看详情

rust编程语言入门之智能指针(代码片段)

...能指针Box<T>:在heap内存上分配值Rc<T>:启用多重所有权的引用计数类型Ref<T>和RefMut<T>,通过RefCell<T>访问:在运行时而不是编译时强制借用规则的类型此外:内部可变模型(interiormutab 查看详情

rust学习笔记1.基础语法(代码片段)

...体的语句和表达式函数返回值7、条件语句8、循环语句9、所有权Rust的所有权内存的分配变量与数据交互方式移动克隆垂悬引用10、切片11、结构体12、枚举类match语法Option枚举类iflet语法13、生命周期生命周期注释函数的生命周期... 查看详情

rust编程语言三(代码片段)

Rust编程语言三fnmain()//所有权规则变量作用域//变量范围是变量的一个属性,其代表变量的可行域,默认从声明变量开始有效直到变量所在域结束。//这里未创建变量s此时s不可用lets="abc";//这里创建了变量s此时s可... 查看详情

rust语言教程-从熟悉的部分开始(代码片段)

Rust语言教程(2)-从熟悉的部分开始虽然有默认不变性还有所有权的问题让Rust一上来用起来有些不同,但是其实大部分语法特点还是我们所熟悉的。我们没必要上来就跟自己死磕,可以先从我们熟悉的部分开始学习。一般... 查看详情

算法学习1773.统计匹配检索规则的物品数量(java/c/c++/python/go/rust)(代码片段)

...ttps://le-yi.blog.csdn.net/博客原创~文章目录1773.统计匹配检索规则的物品数量:样例1样例2提示分析题解javacc++pythongorust原题传送门&# 查看详情

rust有效的数独(代码片段)

判断一个 9x9的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。数字 1-9 在每一行只能出现一次。数字 1-9 在每一列只能出现一次。数字 1-9 在每一个以粗实线分隔的 3x3 ... 查看详情

rust文件读写(代码片段)

https://www.twle.cn/c/yufei/rust/rust-basic-file-input-output.htmlRust文件读写Rust标准库提供了大量的模块和方法用于读写文件。Rust语言使用结构体 File 来描述/展现一个文件。结构体 File 有相关的成员变量或函数用于表示程序可... 查看详情

[易学易懂系列|rustlang语言|零基础|快速入门|(21)|智能指针](代码片段)

...址外,还有额外的其他属性或元数据。在Rust中,因为有所有权和借用的概念,所以引用和智能指针,又有一点不一样。简单来说,智能指针,拥有数据所有权,而引用没有。智能指针分以下几种:1.Box,用于在堆里分配内存。2.Rc,... 查看详情

rust入门基础单向链表(代码片段)

...了很多在其他语言没有的概念,比如生命周期,所有权等。规则多对于C++,大家都是自己申请,自己释放或者由栈帮助释放。对于java、python等语言,其占用由语言自己管理,gc(垃圾回收)经... 查看详情

rust入门基础单向链表(代码片段)

...了很多在其他语言没有的概念,比如生命周期,所有权等。规则多对于C++,大家都是自己申请,自己释放或者由栈帮助释放。对于java、python等语言,其占用由语言自己管理,gc(垃圾回收)经... 查看详情

rust学习笔记-变量和类型(代码片段)

...语言的变量是没有啥可以说道的,无法就是变量命名规则:以字母、下划线和数字组成,并且数字不能开头,没啥好说的。但rust的变量奇怪的地方是,变量不可变譬如这样一个简单到不能再简单的代码fnmain()le... 查看详情

算法leetcode|30.串联所有单词的子串(rust重拳出击)(代码片段)

文章目录30.串联所有单词的子串:样例1:样例2:样例3:提示:分析:题解:rustgoc++pythonjava30.串联所有单词的子串:给定一个字符串s和一个字符串数组words。words中所有字符串长度相同。s中... 查看详情

算法leetcode|30.串联所有单词的子串(rust重拳出击)(代码片段)

文章目录30.串联所有单词的子串:样例1:样例2:样例3:提示:分析:题解:rustgoc++pythonjava30.串联所有单词的子串:给定一个字符串s和一个字符串数组words。words中所有字符串长度相同。s中... 查看详情

rust极简教程(代码片段)

...句循环输出&输入输出输出花括号输出非基础类型输入所有权切片结构体枚举MatchIf-letOption集合vector容器String代码组织命名空间访问其他mod和crate访问权限和关键字访问其他文件中的对象使用第三方库异常处理泛型泛型概念特性... 查看详情