美文网首页Rust
Rust语言教程(8) - 所有权

Rust语言教程(8) - 所有权

作者: Jtag特工 | 来源:发表于2021-01-20 21:03 被阅读0次

Rust语言教程(8) - 所有权

我们从第2讲到第7讲这6讲,在堆上分配了不少对象,但是类似于C++的delete运算符仍然没有出场过。因为Rust有作用域规则,在超出作用域之后会自动释放掉。

移动语义和复制语义

我们来复习下移动语义。

先看一段代码:

    let mut c_0 = Complex{real:0, imagine:0};
    println!("{}",c_0);
    println!("{}",c_0.imagine);
    println!("{:?}",c_0);
    c1.add(c_0);
    println!("{}",c_0);

最后一个println!是编译不过的,因为c1.add调用时,已经将c_0的所有权给拿走了,报错信息如下:

error[E0382]: borrow of moved value: `c_0`
   --> src/main.rs:459:19
    |
454 |     let mut c_0 = Complex{real:0, imagine:0};
    |         ------- move occurs because `c_0` has type `Complex`, which does not implement the `Copy` trait
...
458 |     c1.add(c_0);
    |            --- value moved here
459 |     println!("{}",c_0);
    |                   ^^^ value borrowed here after move

因为默认是移动语义,如果在println!之前把它赋值给另一个变量c_00,则所有权就会被c_00给拿走:

    let mut c_0 = Complex{real:0, imagine:0};
    let c_00 = c_0;
    println!("{}",c_0);
    println!("{}",c_0.imagine);

上面的代码编译不过,报错如下:

error[E0382]: borrow of moved value: `c_0`
   --> src/main.rs:457:19
    |
454 |     let mut c_0 = Complex{real:0, imagine:0};
    |         ------- move occurs because `c_0` has type `Complex`, which does not implement the `Copy` trait
455 |     let c_00 = c_0;
    |                --- value moved here
456 |     println!("{}",c_0);
457 |     println!("{}",c_0.imagine);
    |                   ^^^^^^^^^^^ value borrowed here after move

如果想要将当前的变量赋给新变量不影响使用,那么就可以克隆一个新对象出来。

支持克隆的话,我们需要支持Clone trait,跟Debug一样,让Rust帮我们生成:

    #[derive(Debug,Clone)]
    struct Complex {
        real : i32,
        imagine : i32
    }

然后Complex类就支持clone方法了:

    let c_00 = c_0.clone();

我们通过add方法把c_0的所有权转移走,可以看到clone出来的c_00的所有权不受影响:

    c1.add(c_0);
    println!("{}",c_00);

我们还可以更彻底一些,将移动语义变为复制语义,方法是实现Copy trait:

    #[derive(Debug,Clone,Copy)]
    struct Complex {
        real : i32,
        imagine : i32
    }

现在我们直接赋值,执行的也是复制语义了:

    let mut c_0 = Complex{real:0, imagine:0};
    let c_00 = c_0.clone();
    let c_01 = c_0;

就算是调用add方法,也仍然不会造成所有权转移了:

    c1.add(c_0);
    println!("{}",c_0);

当然,我们也可以不用Rust编译器的derive扩展,自己实现Clone trait和Copy trait.

Clone trait只有一个方法需要被实现:

pub trait Clone {
    fn clone(&self) -> Self;
}

我们来实现一个:

    impl std::clone::Clone for Complex{
        fn clone(&self) -> Self {
            return Complex{real:self.real, imagine: self.imagine};
        }
    }

Copy trait更省事了,它是继承自Clone:

pub trait Copy: Clone { }

所以我们写个空的就好了:

    impl std::marker::Copy for Complex{   
    }

请大家注意std::marker这个包名,这里面的trait其实隐含了对于编译器的配置说明。std::marker::Copy并不只是一个空的继承自Clone的trait,还是对编译器语义转变的提示。

智能指针Box

除了自己管理所有权,我们还可以将所有权交给智能指针Box.

我们来看一个用Box管理我们之前定义的Complex的例子:

    let c5 = Complex{real: 5, imagine: -1};
    let c51 = std::boxed::Box::new(c5);

c51的类型是Box<Complex>类型。

如果要访问c51的域,就像访问c5一样:

    println!("{}",c51.real);

如果要将c51当成Complex对象用的话,需要使用*运算符来访问:

    c1.sub(*c51);
    println!("{}",c1);

引用计数指Rc和Arc

另外一种管理共用所有权的方式是使用引用计数Rc和Arc,它们唯一的区别在于rc是线程不安全的,只能用于单线程情况;而arc是支持线程安全的,但是在单线程情况下没有必要为其花费额外的开销。

Rc的用法跟Box差不多:

    let c41 = std::rc::Rc::new(*c51);
    c1.sub(*c41);
    println!("{}",c1);

我们将其扔到一个线程里去试试效果,还记得写线程的方法吗?

    let child3 = thread::spawn(move || {
        println!("{}",c41);
    });

就会报下面这样的错:

error[E0277]: `Rc<Complex>` cannot be sent between threads safely
   --> src/main.rs:454:18
    |
454 |       let child3 = thread::spawn(move || {
    |  __________________^^^^^^^^^^^^^_-
    | |                  |
    | |                  `Rc<Complex>` cannot be sent between threads safely
455 | |         println!("{}",c41);
456 | |     });

要实现线程安全,我们可以使用std::sync::Arc来替换掉Rc:

    let ac1 = std::sync::Arc::new(c1);
    let child4 = thread::spawn(move || {
        println!("{}",ac1);
    });

小结

这一节如果要记住一个概念的话就记住Box吧,初学阶段默认可以使用它来控制所有权。

相关文章

  • Rust语言教程(8) - 所有权

    Rust语言教程(8) - 所有权 我们从第2讲到第7讲这6讲,在堆上分配了不少对象,但是类似于C++的delet...

  • The Rust programming language 读书

    所有权概念是 Rust 语言的核心功能 Rust 没有垃圾回收(GC)机制 Rust 通过所有权和相关工具保障内存...

  • Rust语言教程(2) - 从熟悉的部分开始

    Rust语言教程(2) - 从熟悉的部分开始 虽然有默认不变性还有所有权的问题让Rust一上来用起来有些不同,但是...

  • Rust 所有权

    认识所有权 所有权是 rust独特的功能,它让 rust无需垃圾回收即可保证内存安全。 什么是所有权 Rust核心...

  • Rust-所有权系统

    今天学习Rust中的所有权系统。按照官方教程所述,所有权系统包含三个部分: 所有权 借用 生命周期 看完发现,C+...

  • 理解 Rust 闭包与环境变量所有权

    本文将以(自认为)最通俗易懂的方式讲述 Rust 中的闭包与环境变量所有权的关系。在现存的类似中文教程中,要么语言...

  • Rust 所有权

    引用[https://www.runoob.com/rust/rust-ownership.html] 所有权规则...

  • 释放堆内存,Rust是怎么做的?所有权!

    本篇谈下Rust语言的核心概念:所有权。 这个概念是支撑Rust在编译期做内存安全检查的核心机制,也正是因为这个特...

  • 2019-08-27

    Rust思考 所有权,Rust通过所有权机制移除了垃圾回收器,但是所有权机制绝非坦途,它对开发人员提出了更高的要求...

  • Rust基础之理解所有权

    什么是所有权 所有权是Rust的一套编译器检查规则,用于管理程序的内存; 例如Java等高级语言采用 garbag...

网友评论

    本文标题:Rust语言教程(8) - 所有权

    本文链接:https://www.haomeiwen.com/subject/ldyoaktx.html