讲透Rust核心概念:所有权

编程入门 行业动态 更新时间:2024-10-24 18:28:56

讲透Rust核心概念:<a href=https://www.elefans.com/category/jswz/34/1740575.html style=所有权"/>

讲透Rust核心概念:所有权

文章目录

    • 可行域
    • 移动和克隆
    • 函数传参
    • 引用与租借

Rust初步上手

程序在内存中运行,所有权指的也是内存的所有权,这个概念被提出的目的,就是让Rust在编译阶段能够更有效地分析内存资源,并优化管理,相当于是静态的垃圾回收。

Rust中的所有权有以下三条规则:

  • 每个值对应一个变量,变量是值的所有者。
  • 这个值在某一时刻,只能有一个所有者。
  • 若所有者不在程序运行范围,该值将被删除。

可行域

在Rust中,任何变量都有其可行域,在作用域中创建的变量,无法渗透到外面,这就是所有权的一个体现。比如在调用下面这个函数时就会报错

fn owner(){{let a = "micro";}println!("{}",a);
}

错误发生在编译期间

>rustc main.rs
error[E0425]: cannot find value `a` in this scope
--> main.rs:10:19|
10 |     println!("{}",a);|                   ^|
help: the binding `a` is available in a different scope in the same function                                              --> main.rs:8:13|
8  |         let a = "micro";|             ^
error: aborting due to previous erro

在编程中难免遇到一些长度不固定的变量,所以程序必须有在执行期间分配内存的能力,不能一切都指望编译期。而且这个分配,既包括新内存的发放,也包括老内存的销毁。

Rust在编译时,会自动在合适的地方添加一些释放资源的函数,从而维护了内存安全。

移动和克隆

下面这种写法大家已经司空见惯了,将5绑定给x,然后再将x绑定给y,其编译运行结果没有任何疑问,就是x=5,y=5

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

但换一种数据类型,结果却报错了,说是把x的值移动给了y,所以x自己就没有了。

fn test_move2(){let x = String::from("micro");let y = x;println!("x={},y={}", x,y)
}

并且报错中还给出了温馨提示,说

help: consider cloning the value if the performance cost is acceptable|
13 |     let y = x.clone();|              ++++++++

即需要调用clone方法来将x值克隆给y,否则就相当于是把x的值移动给了y,所以x自己就没有了。二者的区别相当于是复制和剪切。

在rust中,一些小而直接的数据类型,在数据传递过程中,是默认克隆模式的,比如test_move1中演示的整数,这些类型还包括

  • 所有整数类型,例如 i32 、 u32 、 i64 等。
  • 布尔类型 bool,值为 true 或 false 。
  • 所有浮点类型,f32 和 f64。
  • 字符类型 char。
  • 仅以上类型数据的元组

而其他数据类型,不好意思,默认的就是移动,如果想把其他类型的x复制给y,就要用到下面的形式

fn test_clone(){let x = String::from("micro");let y = x.clone();println!("x={},y={}", x,y);
}

函数传参

在rust中,要知道任何表达式都可以当成是一个函数,是有返回值的。那么自然地,变量在参数传递过程中的特性,也自然要发生在函数传参时,下面的写法有报错了,要求把test_print(x)改写为test_print(x.clone())

fn test_print(s:String){println!("s={}", s);
}fn test_move3(){let x = String::from("micro");test_print(x);println!("x={}", x);
}

但是同样地,如果传递的参数是一个基础类型的变量,那就完全没问题。

fn test_print_int(i:i32){println!("i={}", i);
}fn test_move4(){let x = 5;test_print_int(x);println!("x={}", x);
}

引用与租借

对于复杂变量x,如果想x的值赋予y,要么选择移动,但这样x自己就没了;要么选择克隆,但这个开销就比较大。一个自然的想法就是,能不能把x的地址传给b,这样两人就可以共享一块内存区域。

这种操作rust当然是支持的,名曰引用,只需用到取地址符&,所以下面的函数可以顺利执行

fn test_ref(){let x = String::from("cool");let y = &x;x.push_str("l")println!("x={},y={}", x,y)
}

函数参数传递的道理一样。

但是,正所谓皮之不存毛将焉附,如果y在引用x的值之后,如果x的值被移动给了另外一个变量,那么y,的引用也自然就作废了。

另一方面,y只是引用到了x的地址,但并没有得到这篇内存的所有权,所以y在理论上是不可写入的,这种感觉就像是借书一样,可以随便看,但不能乱写乱画。所以下面的代码报错就是理所当然的了。

实例

fn test_borrow(){let x = String::from("cool");let y = &x;y.push_str("l");println!("{}", y);
}

但是,如果非要更改,那么也不是不可以实现,只需采用mut引用,写成下面这样就可以了,y成了一个可变引用类型的数据。

fn test_borrow2(0[let x = String::from("cool");let y = &mut x;y.push_str("l");println!("{}", y);
])

但可变引用也有一个问题,即只允许可变引用一次。最后,引用不可以作为函数的返回值,因为函数的返回值将不确定这个引用会给谁,可能导致灾难性后果。

这就是Rust语言的风格,严谨而死板,由此带来的好处则是更加安全。

更多推荐

讲透Rust核心概念:所有权

本文发布于:2023-11-16 14:55:01,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1626864.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:所有权   核心   概念   Rust

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!