Rust指针学习笔记(2)——struct和Vec

本文详细探讨了 Rust 中结构体的引用和借用规则,包括不可变引用和可变引用对变量及其成员的影响。举例说明了对结构体本身、结构体成员以及 Vec 类型的引用操作,并解释了 Rust 的借用规则如何防止数据竞争,确保内存安全。同时,讨论了 Vec 类型元素的可变引用与结构体成员引用的区别。
摘要由CSDN通过智能技术生成

一、对结构体本身的引用

先定义一个简单的结构体:

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

在上一篇学习中,有一个关键点,就是如果一个变量被借用以后,那么在借用结束前,该变量的值都不可改变(即使其为mut类型),这样的好处就是避免了数据竞争。同样,这点对于复合类型,如struct,也是一样。下面代码不可编译:

fn main() {
    let mut a = Point { x: 5, y: 5 };
    let b = &a;

    a.x += 10; // Error! cannot assign to `a.x` because it is borrowed

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

同样,一个变量被可变借用结束之前,自身不可使用。不可变借用则无此限制。下面代码可以编译:

    let mut a = Point { x: 5, y: 5 };
    let b = &a;

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

以下代码不可编译:

    let mut a = Point { x: 5, y: 5 };
    let b = &mut a;

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

调整上面最后两行的顺序,则编译通过。

可变借用,可以改变内部成员的值。如下:

    let mut a = Point { x: 5, y: 5 };
    let b = &mut a;

    b.x += 10; // (*b).x += 10;
    println!("{:?}", b);
    println!("{:?}", a);

以上输出a 和 b的值相同:

Point { x: 15, y: 5 }
Point { x: 15, y: 5 }

其中b.x 相当于 (*b).x,编译器进行了隐式解引用。

二、对结构体成员的引用(或指向其成员的指针)

上面都是指向结构体本身的指针,那么能否指向其中的成员呢?又有什么约束和限制?先试一下:

    let a = Point { x: 5, y: 5 };
    let b = &a.x;

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

可以!再试试可变引用:

    let mut a = Point { x: 5, y: 5 };
    let b = &mut a.x;

    *b += 10;

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

输出:

15
Point { x: 15, y: 5 }

也可以!但多加一行代码,如注释行,就会编译报错,仍然是为了避免数据污染。因为b可能会改变a.x的值。

    let mut a = Point { x: 5, y: 5 };
    let b = &mut a.x;

    *b += 10;
    let c = a.x; // Error! cannot use `a.x` because it was mutably borrowed

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

这里面有一个很有意思的点,分别可变借用x 和 y的值会怎么样?允许吗?下面代码:

    let mut a = Point { x: 5, y: 5 };

    let b = &mut a.x;
    *b += 10;

    let c = &mut a.y; // at now b is still active. But you can borrow a.y.
    *c += 20;

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

竟然可以编译和运行!也就是说,对结构体成员的可变引用,是作用在其成员本身的,对一个成员的可变引用,不影响对其它成员的同时的可变引用!

如下代码,有一个所有权的隐藏问题:

    let a = Point { x: 5, y: 5 };

    let c = a.x; // copy a.x to c

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

因为a.x为i32类型,该类型实现了Copy Trait,所以将a.x的值copy到了c。但如果 x为未实现Copy Trait的类型呢?这个下次再讲。

三、Vec类型的引用

深入尝试了struct类型的引用,那么Vec类型如何?以下代码可以运行:

    let mut a = vec![1, 2, 3];
    let b = &mut a;

    b[0] += 10; // (*b)[0] += 10; // It also works!

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

输出:

[11, 2, 3]
[11, 2, 3]

也可以可变引用vec成员的值:

    let mut a = vec![1, 2, 3];
    let b = &mut a[1];

    *b += 10;

    // let c = &a[2]; // cannot borrow `a` as immutable because it is also borrowed as mutable

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

输出:

12
[1, 12, 3]

虽然b 为a[1]的可变引用,可以通过*b修改a[1]的值,但对引用规则的约束,是作用在a上的。否则被注释掉的那一行就会报错。这点与struct类型的成员引用约束是不同的!这估计与rust内部的实现及内存模型有关,有机会去看看rust的源代码才行。

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值