< 返回版块

Binwalker 发表于 2024-01-11 20:33

Tags:生命周期;临时变量

众所周知,值如果没有被绑定到变量上,那么在当前语句结束后就会失效。 但是根据查阅Rust参考中的临时生命周期扩展,得知在某些情况下会扩展临时变量的生命周期。

但是感觉它这个描述有点乱,看半天还是搞不清到底哪些能扩展哪些不能扩展。

比如下面的示例:

use std::ops::Index;

fn main() {
    let s1 = String::from("foo").trim();
    // 取消注释,则上面这行报错
    // println!("{s1}");

    let s2 = &String::from("foo");
    println!("{s2}");

    let s3 = &String::from("baz")[1..2];
    println!("{s3}");

    let s4 = (&String::from("foo")).index(1..2);
    // 取消注释,则上面这行报错
    // println!("{s4}");
}

特别是s3s4,按理说[]就是index的重载,也同样获取了一个切片,但是s3可以,s4就不行。

有没有大佬能简洁的解答一下

评论区

写评论
作者 Binwalker 2024-01-12 21:20

了解了,感谢大佬,不在这种细节上纠结了

--
👇
苦瓜小仔: 已经有 PR 了,但还没合并,在等审核,主要负责这个功能的有中国开发者。

可能会在 edition 2024 落地吧。无论如何这需要一个 edition,因为这影响了现有代码,但该提案会保证在新规则之下有方式重写回当前的 edition 2021 的行为。

It should always be possible to rewrite any expression Rust 2021 expression E with an enclosing block { ... } so that under our proposed rules behaves the same.

edition 2021 的临时生命周期拓展行为就是你链接的 Reference 那个地方(以及 Mara 的那篇文章描写的)。

这只是一个很小的改动,因为临时生命周期只影响临时变量(最常见的是连续方法/函数调用之中的、未被 let 绑定的变量)。

对了,let s3 = &String::from("baz")[1..2]; 中存在一个索引表达式,它的确在当前 Reference 中被指定为可拓展临时生命周期。

If an indexing expression has an extended temporary scope then the indexed expression also has an extended temporary scope.

--
👇
Binwalker: 也就是说新规则的一致性更强更统一,但是目前还在提案阶段未实现,现阶段的话还是不要过于陷入这些细节是吧?

苦瓜小仔 2024-01-12 19:14

已经有 PR 了,但还没合并,在等审核,主要负责这个功能的有中国开发者。

可能会在 edition 2024 落地吧。无论如何这需要一个 edition,因为这影响了现有代码,但该提案会保证在新规则之下有方式重写回当前的 edition 2021 的行为。

It should always be possible to rewrite any expression Rust 2021 expression E with an enclosing block { ... } so that under our proposed rules behaves the same.

edition 2021 的临时生命周期拓展行为就是你链接的 Reference 那个地方(以及 Mara 的那篇文章描写的)。

这只是一个很小的改动,因为临时生命周期只影响临时变量(最常见的是连续方法/函数调用之中的、未被 let 绑定的变量)。

对了,let s3 = &String::from("baz")[1..2]; 中存在一个索引表达式,它的确在当前 Reference 中被指定为可拓展临时生命周期。

If an indexing expression has an extended temporary scope then the indexed expression also has an extended temporary scope.

--
👇
Binwalker: 也就是说新规则的一致性更强更统一,但是目前还在提案阶段未实现,现阶段的话还是不要过于陷入这些细节是吧?

作者 Binwalker 2024-01-12 17:37

感谢大佬回复,也就是说新规则的一致性更强更统一,但是目前还在提案阶段未实现,现阶段的话还是不要过于陷入这些细节是吧?

--
👇
苦瓜小仔: 补充一点(通过从那两个链接中提取的对 OP 直接相关的部分, i.e. 重复链接到那两个链接的子标题而已,因为我知道大部分人没兴趣看全文):

temporary lifetime 有不一致的规则,具体对于 let 语句,表现为

let a = &temporary(); // Extended!
let a = &temporary().field; // Extended!
let a = MyStruct { field: &temporary() }; // Extended!
let a = &MyStruct { field: &temporary() }; // Both extended!
let a = [&temporary()]; // Extended!
let a = { …; &temporary() }; // Extended!

let a = f(&temporary()); // Not extended, because it might not be necessary.
let a = temporary().f(); // Not extended, because it might not be necessary.
let a = temporary() + temporary(); // Not extended, because it might not be necessary

这在 draft RFC 中有总结当前的规则,在这 Rust 2021 temporary value extension rule for variable bindings.

显然 &temprary()[...] 不在上述之列,不过也不难从结果推测它属于可拓展的情况,而 &temprary().index(...) 属于 temporary().f() 连续调用这种不拓展的情况。

(温馨提示:其他语句(涉及 match/if-let)的情况,与这里的 let 语句也不一样。)

无论怎样,都不要依赖这些当前细节,提议的新规则才是重点,根据我的理解(说错不负责),新规则之下,let 语句时,以上 Extended 和 Not extended 都将统一成 Extended ;至于 match/if-let 语句,在 scrutinee 位置上允许拓展临时生命周期,并且中途的临时变量在进入其分支之前被 drop。

苦瓜小仔 2024-01-12 11:24

补充一点(通过从那两个链接中提取的对 OP 直接相关的部分, i.e. 重复链接到那两个链接的子标题而已,因为我知道大部分人没兴趣看全文):

temporary lifetime 有不一致的规则,具体对于 let 语句,表现为

let a = &temporary(); // Extended!
let a = &temporary().field; // Extended!
let a = MyStruct { field: &temporary() }; // Extended!
let a = &MyStruct { field: &temporary() }; // Both extended!
let a = [&temporary()]; // Extended!
let a = { …; &temporary() }; // Extended!

let a = f(&temporary()); // Not extended, because it might not be necessary.
let a = temporary().f(); // Not extended, because it might not be necessary.
let a = temporary() + temporary(); // Not extended, because it might not be necessary

这在 draft RFC 中有总结当前的规则,在这 Rust 2021 temporary value extension rule for variable bindings.

显然 &temprary()[...] 不在上述之列,不过也不难从结果推测它属于可拓展的情况,而 &temprary().index(...) 属于 temporary().f() 连续调用这种不拓展的情况。

(温馨提示:其他语句(涉及 match/if-let)的情况,与这里的 let 语句也不一样。)

无论怎样,都不要依赖这些当前细节,提议的新规则才是重点,根据我的理解(说错不负责),新规则之下,let 语句时,以上 Extended 和 Not extended 都将统一成 Extended ;至于 match/if-let 语句,在 scrutinee 位置上允许拓展临时生命周期,并且中途的临时变量在进入其分支之前被 drop。

苦瓜小仔 2024-01-11 21:18

没有简洁的回答,当前临时生命周期的规则就是不一致。想详细知道更多,去看

我的总结是,基于当前规则,出错的原因是因为连续的调用之中产生的临时变量在赋值语句末尾 drop(或者说,复杂情况时,编译器不会拓展临时作用域);通过的原因是,对于简单的赋值情况,编译器选择拓展临时作用域(let s2 = &String::from("foo"); 和 &String::from("baz")[1..2] 被视为简单情况,尤其对于后者,我怀疑索引是简单的 primitive 操作,所以通过,相应的 (&String::from("foo")).index(1..2) 不是 primitive 操作,所以不行)。

1 共 5 条评论, 1 页