简单理解rust的所有权

·  阅读 476

如何理解rust的所有权

所有权

如何理解rust的所有权机制,会让我们更深入地理解rust优秀的内存管理机制以及比C++更为安全的代码结构。

首先以C语言举个例子,如果我们要定义一个指针对象:Fun *p = do_something(),接下来使用这个对象do_otherthing(p);在函数do_otheringp使用过后,我们不知道函数内部对p做了那些改变,碰巧如果该函数实现极为复杂,对p使用了多次,这会对程序的维护运行引入不小的麻烦。如果p在函数内部被释放,而外部词法作用域并不知道,这样会形成一个空指针,在这样的情况如果再次使用p,会带来错误。

在此基础上C++引入了智能指针来减少了这类bug的出现,此时也出现了所有权的概念。

rust中的所有权代表了以下概念:

  • 每个值在Rust中都有一个变量来管理,这个变量便是这块内存的管理者。
  • 每个值或者说每块内存在一个时间点上只能有一个拥有所有权的拥有者。
  • 当变量所在的作用域结束的时候,变量会被释放。
fn main(){
    let mut s = String::from("hello");
    s.push_str("juejin");
    println!("{:?}",s);
}
复制代码

在上面这一小段代码中,定义了一个String类型的字符串,接着对他进行修改,然后输出。main函数结束,s的生命周期也结束,被内存释放。

生命周期是rust对于管理所有权的一个扩充

如果在上面第一行代码后面加一行:

let s1 = s;
复制代码

接下来对与s的使用便会报错,提示value used here after move,这是因为s的所有权转移给了s1,这里原因还有String类型没有实现Copytrait。实现了Copytrait的类型,会在赋值时拷贝一块内存给赋值的变量,这样两个指向不同的内存地址,也就可以共存。rust中一些简单的变量都实现了Copytrait,便于开发使用。如数字,布尔等。

智能指针

Box

rust中有三中简单的智能指针Box<T>,Rc<T>,Cell<T>。这三种与所有权语义都有联系。

首先是Boxrust中所有权值默认在栈上分配。而我们可以通过Box指针将值包装,使他在堆上分配内存。

fn main(){
    let num = Box::new(1);
    let i = &num;
    // let k = num; Error
    let j = &num;
    println!("{:?}",num);
    println!("{}",i)
}
复制代码

由于Box自身实现了Deref trait,所以我们可以通过&解引用获得内部的值1.而同时Box也实现了Droptrait,这样会在作用域结束的时候自动销毁。销毁过程是先释放堆上的数据,后释放值。使用Box的情况主要在于要知道一个类型在定义时占用多少内存空间,这时使用Box便可以轻松确定。Box指针本身在栈上,里面的数据在堆上分配。

Rc

Rc指针,顾名思义,是一个引用计数指针。Rc所包裹的为不可变类型,他允许同时存在多个引用,但不可对内部进行修改。Rc通过clone()方法来增加引用。

use std::rc::Rc;
let arr = Rc::new(vec![1,2,3]);
let arr_1 = arr.clone();
let arr_2 = Rc::clone(&arr);
复制代码

如果我们需要一个不可变变量的多次引用,并且修改。可使用RefCell

Cell

首先说一下内部可变性。即多个引用共享,同时存在。共享引用可以修改内部数据。虽然rust所有权的核心是共享不可变,但这两个并不冲突。因为之前的共享不可变是编译阶段编译器的检查,使用Cell可以通过返回包裹的值的一个拷贝,在对其进行修改,再用新值替换旧值。Cell的使用有一个要求是Cell<T>中T必须实现Copytrait。这也对应了前面拷贝的语义。

let num = Cell::new(String::from("12"));
num.set(String::from("22"));
println!("{}",num.get());
// Error
// note: the following trait bounds were not satisfied:
//            `String: Copy`
let num_1 = Cell::new(1);
num_1.set(123);
println!("{}",num_1.get());
复制代码
分类:
后端
标签:
分类:
后端
标签:
收藏成功!
已添加到「」, 点击更改