gpt4 book ai didi

rust - 如何将创建包含具有生命周期的 Cell 的结构的函数传递给另一个函数?

转载 作者:行者123 更新时间:2023-11-29 07:42:52 25 4
gpt4 key购买 nike

我正在尝试将构造函数作为参数传递给另一个函数。该函数创建一个具有关联生命周期的结构。我需要从这个指针创建一个结构之后我已经创建了一些其他的对象,这个结构然后可以引用。下面的示例似乎有效:

struct Bar<'a> {
number: Option<&'a usize>,
}

impl<'a> Bar<'a> {
pub fn new() -> Bar<'a> {
Bar { number: None }
}
}

fn foo<'a, F>(func: &F)
where
F: Fn() -> Bar<'a>,
{
let number = 42;
let mut bar = (func)();
bar.number = Some(&number);
}

fn main() {
foo(&Bar::new);
}

当我为内部可变性添加一个 Cell 时,它不会编译:

use std::cell::Cell;

struct Bar<'a> {
number: Cell<Option<&'a usize>>,
}

impl<'a> Bar<'a> {
pub fn new() -> Bar<'a> {
Bar {
number: Cell::new(None),
}
}
}

fn foo<'a, F>(func: &F)
where
F: Fn() -> Bar<'a>,
{
let number = 42;
let bar = (func)();
bar.number.set(Some(&number));
}

fn main() {
foo(&Bar::new);
}

给我以下错误:

error[E0597]: `number` does not live long enough
--> src/main.rs:21:26
|
21 | bar.number.set(Some(&number));
| ^^^^^^ borrowed value does not live long enough
22 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 15:1...
--> src/main.rs:15:1
|
15 | / fn foo<'a, F>(func: &F)
16 | | where
17 | | F: Fn() -> Bar<'a>,
18 | | {
... |
21 | | bar.number.set(Some(&number));
22 | | }
| |_^

为什么第一个示例有效而第二个无效?有没有一种方法可以指定作用域 let mut bar 存在的生命周期,直到函数结束,而不是包含整个函数的 'a ?如果没有非词法生命周期或更高种类的类型构造函数等,这是不可能的吗?

最佳答案

编译器比您想象的要聪明。它防止您引入内存不安全:

fn foo<'a, F>(func: &F)
where
F: Fn() -> Bar<'a>,
{
let number = 42;
let bar = (func)();
bar.number.set(Some(&number));
}

此代码表示 foo 的调用者可以为 'a 指定生命周期,但随后方法的主体将引用存储到值中。不能保证存储的引用能活那么久。作为一个明显的例子,调用者可能需要 'a == 'static ,但函数无法完成:

fn b() -> Bar<'static> {
Bar {
number: Cell::new(None),
}
}

fn main() {
foo(&b);
}

请注意,这与闭包或函数没有任何关系:

use std::cell::Cell;

fn main() {
let number = Cell::new(None);
let x = 1;
number.set(Some(&x));
let y = 2;
number.set(Some(&y));
}
error[E0597]: `x` does not live long enough
--> src/main.rs:6:22
|
6 | number.set(Some(&x));
| ^ borrowed value does not live long enough
...
9 | }
| - `x` dropped here while still borrowed
|
= note: values in a scope are dropped in the opposite order they are created

Why did the first example work and not the second?

因为编译器知道 Cell (真的 UnsafeCell )需要考虑到您将在创建的类型中存储值的可能性。

来自 the Nomicon ,强调我的:

  • UnsafeCell<T>, Cell<T>, RefCell<T>, Mutex<T> and all other interior mutability types are invariant over T (as is *mut T by metaphor)

Variance是一个密集的话题,我无法简洁地解释。

@trentcl提供了这个示例,表明您的原始代码可能没有按照您的想法进行。

没有 Cell ,编译器知道自动将返回类型的生命周期调整为稍微短一点的生命周期是安全的。如果我们强制类型更长 'a , 然而,我们得到了同样的错误:

fn foo<'a, F>(func: F)
where
F: Fn() -> Bar<'a>,
{
let number = 42;
let mut bar: Bar<'a> = func();
// ^^^^^^^
bar.number = Some(&number);
}
error[E0597]: `number` does not live long enough
--> src/main.rs:17:24
|
17 | bar.number = Some(&number);
| ^^^^^^ borrowed value does not live long enough
18 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 11:1...
--> src/main.rs:11:1
|
11 | / fn foo<'a, F>(func: F)
12 | | where
13 | | F: Fn() -> Bar<'a>,
14 | | {
... |
17 | | bar.number = Some(&number);
18 | | }
| |_^

Is this not possible without [...]

是的,但我不确定它到底是什么。我相信它需要 generic associated types (GAT) from RFC 1598 .

我的第一个想法是尝试更高等级的特征界限 (HRTB):

fn foo<F>(func: F)
where
F: for<'a> Fn() -> Bar<'a>,
{
let number = 42;
let bar = func();
bar.number.set(Some(&number));
}

这会触发 E0582 :

error[E0582]: binding for associated type `Output` references lifetime `'a`, which does not appear in the trait input types
--> src/main.rs:17:25
|
17 | F: for <'a> Fn() -> Bar<'a>,
| ^^^^^^^

老实说,根据提供的示例,我看不到代码中的值。如果您要返回 Bar通过值,您可以使其可变,从而消除对内部可变性的任何需求。

您还可以更改闭包以根据需要获取值:

fn foo<F>(func: F)
where
F: for<'a> Fn(&'a i32) -> Bar<'a>,
{
let number = 42;
let bar = func(&number);
}

另见:

关于rust - 如何将创建包含具有生命周期的 Cell 的结构的函数传递给另一个函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51079507/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com