Rust学习笔记(6)——引用情况struct中所有权的转移问题

还是先定义struct:

#[derive(Debug)]
struct Point {
    x: Box<i32>,
    y: Box<i32>,
    z: i32,
}

通过可变引用,可以改变成员的值,与结构体成员为简单的i32类型一致,除了不能对Box类型使用 “+=”符号以外。

fn main() {
    let mut a = Point {
        x: Box::new(3),
        y: Box::new(4),
        z: 5,
    };
    let b = &mut a;

    b.x = Box::new(10); // b.x += Box::new(10); // cannot use "+="

    println!("{:?}", b);
    println!("{:?}", a);
}

同样,也可以借用其中的值并改变:

    let b = &mut a.x;

    *b = Box::new(10);

下面代码多次引用,也可以编译,编译器自动隐式调用了多次自动解引用:

    let mut b = &mut a;

    let c = &mut b;

    *c.x += 10;

上一篇提到,struct允许转移成员的所有权。但如果一个struct变量已经被借用了,就不允许这样做了:

    let a = Point {
        x: Box::new(3),
        y: Box::new(4),
        z: 5,
    };

    let b = &a;
    let c = a.x;

最后一行会报错:“cannot move out of `a.x` because it is borrowed”。举一个不太恰当的例子,这就像你已经把房子租出去了,然后还想把其中一间卖掉,当然不行了。如果允许这样做,那么在使用b时就会出现bug了。

同样,b作为a的引用,对a代表的数据及a的成员数据没有所有权,也不允许下面的操作:

    let b = &a;

    let c = b.x; // Error! cannot move out of `b.x` which is behind a shared reference

即使把b变成a的可变引用也是一样报错。这就像你租了一套房子,然后想把其中一间卖掉,当然不行了。在实验的过程中还出现了2种情况,第一种:

    let b = &a;

    let c = b;

    println!("{:?}", c);

    println!("{:?}", b);

    println!("{:?}", a);

把b赋值给c,b仍然可用,那么根据rust的规则推测,这时应该是把b的值copy to c了。通用推论是不可变引用(指针)是实现了copy trait的。但如果换成不可变指针,就不行了:

    let b = &mut a;

    let c = b;

    println!("{:?}", c);

    println!("{:?}", b); // Error! borrow of moved value: `b`!

    println!("{:?}", a);

b的值 move to c 了。看来rust对于不可变引用和可变引用的底层处理机制是完全不同的。也不允许对结构体和其成员分别同时可变引用,以下代码无法编译:

    let b = &mut a;

    let c = &mut a.x; // Error!  

    let c = &a.x; // Error!

如果是不可变引用,则支持连环引用,也支持同时引用结构体本身和其成员,如下代码可编译:

    let b = &a;

    let c = &b;

    let c = &a.x;

    let c = &b.x;

以下代码无法编译:

    let b = &mut a;

    let c = &mut b; // cannot borrow `b` as mutable, as it is not declared as mutable

如果 将b声明为mut即可编译。但诡异的是,下面代码可以编译!

    let b = &mut a;
    let c = &mut b.x;

这里,b为a的可变借用,但b本身不可变,但可以可变借用b的成员(当然不可变借用更是OK的,尝试去掉第二行的mut试试)!还可以同时可变借用其不同成员,以下代码也可编译通过:

    let b = &mut a;
    let c = &mut b.x;
    // let d = &b.y; // It also works!
    let d = &mut b.y;

    println!("{:?}", c);
    println!("{:?}", d);
    println!("{:?}", b);
    println!("{:?}", a);

以下也可以编译通过:

    let mut a = Point {
        x: Box::new(3),
        y: Box::new(4),
        z: 5,
    };

    let b = &mut a.x;
    let c = &mut a.y;

直接可变借用结构体的不同成员,或者通过可变借用再进行不同成员的可变借用,竟然都可以!(不可变借用更是OK)。可变借用,也可以改变成员的值:

    let mut a = Point {
        x: Box::new(3),
        y: Box::new(4),
        z: 5,
    };

    let b = &mut a.x;
    let c = &mut a.y;
    *b = Box::new(30);

    println!("{:?}", c);
    println!("{:?}", b);
    println!("{:?}", a);

输出:

4
30
Point { x: 30, y: 4, z: 5 }

总结一下,结构体不同成员间的引用/可变引用是没有相互影响的,但不可和结构体本身同时存在可变引用。

下面代码也可以正常运行:

    let mut a = Point {
        x: Box::new(3),
        y: Box::new(4),
        z: 5,
    };

    let b = &mut a;
    let c = &mut b.x;
    *c = Box::new(20);

    println!("{:?}", c);
    println!("{:?}", b);
    println!("{:?}", a);

输出:

20
Point { x: 20, y: 4, z: 5 }
Point { x: 20, y: 4, z: 5 }

b是a的可变引用,c又是b成员x的可变引用,改变c指向的值,也就是改变了a成员x的值。

最后这一点,b是a的可变引用,前面提到,不能再将b.x move to c. 但仍然可以通过可变引用去改变其成员的值。这点对于使用struct实现链表尤其有用。

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值