< 返回版块

ziyouwa 发表于 2021-08-19 15:14

Tags:rust mutable immutable borow

看到一个小例子,是可以编译通过执行的。没想明白的是vec0变量,同时存在了不可变借用(第三行println! )和可变借用(第4行)。这不是跟rust的借用规则矛盾吗?借用规则是:同时存在一个可变借用,或多个不可变借用。我理解错了吗?

代码如下:

fn main () {
    let mut vec0 = Vec::new();

    println!("{} has length {} content `{:?}`", "vec0", vec0.len(), vec0);

    let  vec1 = fill_vec(&mut vec0);
    vec1.push(88);

    println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1);
}

fn fill_vec(vec: &mut Vec<i32>) -> &mut Vec<i32> {

    vec.push(22);
    vec.push(44);
    vec.push(66);

    vec
}

评论区

写评论
作者 ziyouwa 2021-08-20 09:27

第一句let mut vec0漏了mut,已改正。经过讨论,对printlin!的认识更清楚了,感谢各位大神耐心回答!

Aya0wind 2021-08-19 17:05

你可以把借用本身也看成一个变量,一个借用也是有其生命周期的,而对于println宏,其内部确实对vec0产生了一个不可变借用,但是这个借用在执行完这个宏块之后就无了,所以执行到后面产生可变借用的时候,vec0是不存在任何借用的,当然满足借用规则。

bzeuy 2021-08-19 16:43

println! 展开后是一个block

eweca-d 2021-08-19 16:39

首先你这个编译不过,是因为let vec0 = Vec::new();这里,vec0是不可变变量,借不出可变引用&mut vec0。不过这个是细枝末节的小问题。

至于println,你可以把println!当成是一个函数,你写进去的变量都会被它拿走一个不可变引用。而rust是移动语义,你借走了不可变引用,不可变引用权就进入了print函数里,等到这个函数结束,不可变引用就drop掉了。所以在 let vec1 = fill_vec(&mut vec0);,实际上已经没有引用了,所以可以借出不可变引用。

当然也有可能println是生成了临时的不可变借用,由于没有绑定到变量上,所以句末就直接丢弃了。

我不知道println是怎么运作的,但是我知道println的行为就是,尝试借出不可变引用,打印后丢弃不可变引用。这是肯定的。


此外还有一种你可能没发现的:

fn main () {
    let mut vec0 = Vec::new();

    println!("{} has length {} content `{:?}`", "vec0", vec0.len(), vec0);

    let  vec1 = fill_vec(&mut vec0);
    vec1.push(88);

    // println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1);
    
    // OK
    println!("{} has length {} content `{:?}`", "vec0", vec0.len(), vec0);

    // error
    // vec1.push(88);
}

fn fill_vec(vec: &mut Vec<i32>) -> &mut Vec<i32> {

    vec.push(22);
    vec.push(44);
    vec.push(66);

    vec
}

这种其实也是可行的,就是明明你持有不可变引用,但是后面println可以借出不可变引用。这是因为编译器会自动分析出,在println之后是没有使用到可变引用vec1的,所以就没问题。而在最后一行如果加上对vec1的引用,编译器就会报错,这就是借用规则的体现了。

--
👇
ziyouwa: 对vec0来说,最早应该是在第4行调用fill_vec之后才丢弃的

--
👇
eweca-d: 你这些都是临时借用,这一行结束后就丢弃了。

作者 ziyouwa 2021-08-19 15:50

对vec0来说,最早应该是在第4行调用fill_vec之后才丢弃的

--
👇
eweca-d: 你这些都是临时借用,这一行结束后就丢弃了。

作者 ziyouwa 2021-08-19 15:47

playground确实编译不过。 我在https://github.com/rust-lang/rustlings这个里面确实是通过了。exercises/move_semantics/move_semantics2.rs

puzc1993 2021-08-19 15:38

这段代码一定编译不通过

Compiling playground v0.0.1 (/playground) error[E0596]: cannot borrow vec0 as mutable, as it is not declared as mutable --> src/main.rs:6:26 | 2 | let vec0 = Vec::new(); | ---- help: consider changing this to be mutable: mut vec0 ... 6 | let vec1 = fill_vec(&mut vec0); | ^^^^^^^^^ cannot borrow as mutable

error: aborting due to previous error

For more information about this error, try rustc --explain E0596. error: could not compile playground

To learn more, run the command again with --verbose.

eweca-d 2021-08-19 15:37

你这些都是临时借用,这一行结束后就丢弃了。

1 共 8 条评论, 1 页