可变借用自动更改为不可变? [英] Mutable borrow automatically changes to immutable?

查看:39
本文介绍了可变借用自动更改为不可变?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

似乎u,一个可变借用,在

中自动变为不可变

let v = &*u;

uv 都是不可变的借用引用,因此它们都是允许的.

使用 std::ascii::AsciiExt;fn show(a: &str) {println!("a={}", a);}fn 主(){让 mut t = String::new();t.push('s');让 u = &mut t;u.make_ascii_uppercase();//你在这里真的是可变的让 v = &*u;//你变得不可变以允许这样做吗?显示(u);//u 和 v 现在都可以访问了!展示(五);}

输出:

a=Sa=S

如果我尝试在

之后使用 u 作为可变借用

show(v);

编译器会记得

let v = &*u;

确实不允许:

不能借用 `*u` 作为可变的,因为它也被借用为不可变的

这是一个错误还是真的有一些当不再需要可变性时自动将可变借用转换为不可变"的原则?我使用的是 Rust 1.13.0.

解决方案

可变引用可以被永久地借用,但这不是这里发生的事情.

当使用 & 形成引用时,您需要明确说明可变性;除非您指定 &mut,否则它将是一个不可变的引用.

<小时>

您的示例可以简化为:

使用 std::ascii::AsciiExt;fn 主(){让 mut t = "s".to_string();让 u = &mut t;u.make_ascii_uppercase();让 v = &*u;让 () = v;}

最后一行是一个技巧,让编译器告诉我们(在错误消息中)v 的类型是什么.它报告:

错误[E0308]:类型不匹配--><匿名>:9:9|9 |让 () = v;|^^ 预期参考,找到 ()|= 注意:预期类型`&std::string::String`= 注意:找到类型`()`

这里有:

  • u:不可变绑定,是t
  • 的可变借用
  • v:不可变绑定,是t通过u
  • 的不可变重新借用

但是,如果我将 v 行更改为 let v = &mut *u;,那么我会得到 预期类型 '&mut std::string::String' 然后我们有:

  • u:不可变绑定,是t
  • 的可变借用
  • v:不可变绑定,是t通过u
  • 的可变重借
<小时>

这里的重要概念是重新借用,这就是&*u&mut *u 的含义.重新借用允许从现有引用形成新引用:

  • 重新借用访问最初借用的变量
  • 在重新借用的生命周期内,借用形成它的引用

再借用规则相对简单,它们反映了借用规则:

  • 如果你从一个不可变的引用开始:
    • 您只能将其作为不可变引用重新借用,如果您愿意,可以进行多个并发的不可变重新借用
  • 如果你从一个可变引用开始:
    • 您可以单独将其重新借用为可变引用
    • 或者您可以将其作为不可变引用重新借用,如果您愿意,可以使用多个并发不可变重新借用

有趣的是,重新借用的引用可以比它形成的引用存活的时间更长:

fn main() {让 mut t = "s".to_string();让 v;{让 u = &mut t;v = &mut *u;}v.make_ascii_uppercase();展示(五);}

这是确保您可以从函数返回引用所必需的;当然.

因此,最终,编译器会跟踪重新借用到原始借用值;然而,由于重新借用机制,它允许形成对该原始值的不可变引用,即使可变引用在范围内......并且只需确保此可变引用在新的不可变引用的生命周期内不可用.<小时>

当一个函数接受一个引用时,编译器会自动在调用点引入一个具有适当可变性的重新借用;这就是 show 在这里发生的事情:show(u) 真的是 show(&*u) 为函数调用的持续时间.

It seems that u, a mutable borrow, becomes automatically immutable in

let v = &*u;

Both u and v are then immutable borrowed references so they are both allowed.

use std::ascii::AsciiExt;

fn show(a: &str) {
    println!("a={}", a);
}

fn main() {
    let mut t = String::new();
    t.push('s');
    let u = &mut t;
    u.make_ascii_uppercase(); // u is really mutable here
    let v = &*u; // u became immutable to allow this?
    show(u); // both u and v are now accessible!
    show(v);
}

Outputs:

a=S
a=S

If I try to use u as a mutable borrow after

show(v);

compiler will recall that

let v = &*u;

is really not allowed:

cannot borrow `*u` as mutable because it is also borrowed as immutable

Is it a bug or is there really some "automatically convert mutable borrow to immutable when mutability is no longer needed" principle? I am using Rust 1.13.0.

解决方案

A mutable reference can be borrowed immutably, however this is not what is happening here.

When forming a reference with &, you need to be explicit about mutability; unless you specify &mut it will be an immutable reference.


Your example can be reduced to:

use std::ascii::AsciiExt;

fn main() {
    let mut t = "s".to_string();
    let u = &mut t;
    u.make_ascii_uppercase();
    let v = &*u;

    let () = v;
}

The last line is a trick to get the compiler to tell us (in the error message) what the type of v is. It reports:

error[E0308]: mismatched types
 --> <anon>:9:9
  |
9 |     let () = v;
  |         ^^ expected reference, found ()
  |
  = note: expected type `&std::string::String`
  = note:    found type `()`

Here we have:

  • u: an immutable binding, which is a mutable borrow of t
  • v: an immutable binding, which is an immutable re-borrow of t through u

If, however, I change the v line to let v = &mut *u;, then I get expected type '&mut std::string::String' and then we have:

  • u: an immutable binding, which is a mutable borrow of t
  • v: an immutable binding, which is a mutable re-borrow of t through u

The important concept here is re-borrowing, which is what &*u and &mut *u are about. Re-borrowing allows forming a new reference from an existing reference:

  • a re-borrow access the initially borrowed variable
  • for the lifetime of the re-borrow, the reference from which it is formed is borrowed

The re-borrowing rules are relatively simple, they mirror the borrowing rules:

  • if you start from an immutable reference:
    • you can re-borrow it only as an immutable reference, with multiple concurrent immutable re-borrow if you wish
  • if you start from a mutable reference:
    • you can either re-borrow it as a mutable reference, exclusively
    • or you can re-borrow it as an immutable reference, with multiple concurrent immutable re-borrow if you wish

It is interesting to note that a re-borrowed reference can live longer than the reference it was formed from:

fn main() {
    let mut t = "s".to_string();

    let v;
    {
        let u = &mut t;
        v = &mut *u;
    }

    v.make_ascii_uppercase();
    show(v);
}

This is necessary to ensure that you can return a reference from functions; of course.

So, ultimately, a re-borrow is tracked down to the original borrowed value by the compiler; however, due the re-borrowing mechanics it allows forming an immutable reference to this original value even though a mutable reference is in scope... and simply make sure that this mutable reference is unusable for the lifetime of the new immutable reference.


When a function takes a reference, the compiler automatically introduces a re-borrow at the call site with the appropriate mutability; this is what happens with show here: show(u) really is show(&*u) with a new immutable reference formed for the duration of the function call.

这篇关于可变借用自动更改为不可变?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆