- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试将构造函数作为参数传递给另一个函数。该函数创建一个具有关联生命周期的结构。我需要从这个指针创建一个结构之后我已经创建了一些其他的对象,这个结构然后可以引用。下面的示例似乎有效:
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 overT
(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/
我正在开发一个使用多个 turtle 的滚动游戏。玩家 turtle 根据按键命令在 Y 轴上移动。当危害和好处在 X 轴上移动时,然后循环并改变 Y 轴位置。我尝试定义一个名为 colliding(
我不明白为什么他们不接受这个作为解决方案,他们说这是一个错误的答案:- #include int main(void) { int val=0; printf("Input:- \n
我正在使用基于表单的身份验证。 我有一个注销链接,如下所示: 以及对应的注销方法: public String logout() { FacesContext.getCurren
在 IIS7 应用程序池中有一个设置 Idle-time out 默认是 20 分钟,其中说: Amount of time(in minutes) a worker process will rem
我是一名优秀的程序员,十分优秀!