【问题标题】:Why is it legal to borrow a temporary?为什么临时借用是合法的?
【发布时间】:2018-05-19 14:58:18
【问题描述】:

来自 C++,我很惊讶这段代码在 Rust 中有效:

let x = &mut String::new();
x.push_str("Hello!");

在 C++ 中,您不能获取临时地址,并且临时地址不会超过它出现的表达式。

临时在 Rust 中存在多长时间?而且由于x只是借用,那么字符串的所有者是谁?

【问题讨论】:

  • 在 C++ 中,您不能获取临时地址 — 我不懂 C++,但这总是正确的吗? Does a const reference prolong the life of a temporary?
  • @Shepmaster &xx 的地址,我相信这对临时人员永远无效。我可能应该将此与创建对临时对象的引用进行比较,这确实是可能的,甚至延长了临时对象的寿命,因此总体而言,其行为实际上与 Rust 所做的非常相似。
  • @SvenMarnach:你可以完美地在 C++ 中获取临时地址,struct T { T* me() { return this; } }; 将返回给你T 实例的地址,无论它是否是临时地址。此外,C++ 允许将 const 引用或 r 值引用绑定到临时对象,并且对它的引用只不过是变相的指针。
  • @MatthieuM。是的,我做的比较没有任何意义。我应该将它与在 C++ 中创建引用进行比较,而不是仅仅因为语法看起来相似而与 &temp 进行比较。
  • @SvenMarnach:不用担心 :) 语法很相似,效果也很相似(因为引用是指针),所以看起来很自然的错误。只是 Stroustrup 以某种方式决定了一些允许的事情,而另一些则不是因为他有一种直觉,这很容易出错......事后看来,缺乏一致性可能更令人困惑:)

标签: rust temporary


【解决方案1】:

为什么临时借用是合法的?

它是合法的,因为它在 C++ 中是非法的——because someone said that's how it should be

临时在 Rust 中存在多长时间?既然x只是借用,那么字符串的所有者是谁?

The reference says:

表达式的临时范围是 包含表达式的最小作用域,用于以下之一:

  • 整个函数体。
  • 声明。
  • ifwhileloop 表达式的主体。
  • if 表达式的 else 块。
  • ifwhile 表达式或 match 的条件表达式 守卫。
  • 匹配臂的表达式。
  • lazy boolean expression 的第二个操作数。

基本上,您可以将您的代码视为:

let mut a_variable_you_cant_see = String::new();
let x = &mut a_variable_you_cant_see;
x.push_str("Hello!");

另见:

【讨论】:

【解决方案2】:

来自Rust Reference

临时生命周期

在大多数位置表达式上下文中使用值表达式时,会创建一个临时未命名的内存位置,并初始化为该值,而表达式的计算结果会改为该位置

这适用,因为String::new() 是一个值表达式,并且在&mut 的下方,它位于位置表达式上下文中。现在引用运算符只需要经过这个临时内存位置,就变成了整个右边的值(包括&mut)。

但是,当创建分配给 let 声明的临时值表达式时,将使用封闭块的生命周期创建临时值

因为它被分配给变量,所以它有一个生命周期,直到封闭块结束。

这也回答了this question关于两者的区别

let a = &String::from("abcdefg"); // ok!

let a = String::from("abcdefg").as_str(); // compile error

在第二个变体中,临时变量被传递到as_str(),因此它的生命周期在语句的末尾结束。

【讨论】:

  • In the second variant the temporary is passed into as_str(), so its lifetime ends at the end of the statement.。现在我明白了,谢谢!
  • It is not a compile error,作为一个 C/C++ 用户,这是双重困惑。
  • @arunanshub 确实是编译器错误。检查play.rust-lang.org/… 在您的代码中,每个let a 声明都会影响之前的声明。
  • @sateesh 抱歉。我没有注意使用变量的情况。现在我懂了。谢谢。
【解决方案3】:

Rust 的 MIR 提供了一些关于临时对象性质的见解;考虑以下简化情况:

fn main() {
    let foo = &String::new();
}

以及它产生的 MIR(标准 cmets 替换为我的):

fn main() -> () {
    let mut _0: ();
    scope 1 {
        let _1: &std::string::String; // the reference is declared
    }
    scope 2 {
    }
    let mut _2: std::string::String; // the owner is declared

    bb0: {                              
        StorageLive(_1); // the reference becomes applicable
        StorageLive(_2); // the owner becomes applicable
        _2 = const std::string::String::new() -> bb1; // the owner gets a value; go to basic block 1
    }

    bb1: {
        _1 = &_2; // the reference now points to the owner
        _0 = ();
        StorageDead(_1); // the reference is no longer applicable
        drop(_2) -> bb2; // the owner's value is dropped; go to basic block 2
    }

    bb2: {
        StorageDead(_2); // the owner is no longer applicable
        return;
    }
}

您可以看到“不可见”的所有者在为其分配引用之前接收到一个值,并且该引用在所有者之前被删除,正如预期的那样。

我不确定的是为什么会有一个看似无用的scope 2,以及为什么没有将所有者放在任何范围内;我怀疑 MIR 还没有 100% 准备好。

【讨论】:

  • 谢谢!这表明 Shepmaster 给出的等效代码不仅仅是一个类比,而是字面上正在发生的事情。中间表示总是倾向于包含许多看似无用的位,这些位在后期阶段被优化。我也喜欢在某个随机点分配的可变空元组。
  • @SvenMarnach 如果我没记错的话,_0 元组只是main() 的返回值;不过,这仍然不是最有趣的信息:)。
猜你喜欢
  • 1970-01-01
  • 2011-05-17
  • 1970-01-01
  • 2019-04-04
  • 2015-06-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-28
  • 2015-10-19
相关资源
最近更新 更多