我有下面的代码片段(生命周期尝试省略):
pub struct NamedArgument<T>(pub(in crate) &'static str, pub(in crate) T);
pub struct LoggedArgument<T>(pub(in crate) &'static str, pub(in crate) T);
impl<T> NamedArgument<T> {
pub fn log_with<T2, F>(self, log_level: log::Level, transform: F) -> LoggedArgument<T>
where
T2: Display,
F: FnOnce(&T) -> T2,
{
log::log!(log_level, "{} = {}", self.0, transform(&self.1));
LoggedArgument(self.0, self.1)
}
}
字符串
我将其命名为:
fn foo(argument: NamedArgument<impl AsRef<str>>) {
argument.log_with(log::Level::Info, |s| s.as_ref());
}
型
但这会失败,并出现以下错误:
error: lifetime may not live long enough
--> src/lib.rs:20:45
|
20 | argument.log_with(log::Level::Info, |s| s.as_ref());
| -- ^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
| ||
| |return type of closure is &'2 str
| has type `&'1 impl AsRef<str>`
型
我确实理解这个问题(至少我是这么认为的),但发现自己无法正确地指定生命周期。本质上,我需要指定返回的T2
的寿命超过传入闭包的参数。我已经尝试了几十种生存期规范的组合,包括为函数trait使用更高级别的trait边界。
如何指定使此系统正常工作所需的生存期?
2条答案
按热度按时间omvjsjqw1#
不幸的是,这在当前的Rust中是不可能的。您有两种选择:
1.定义将
&T
转换为&T2
的函数。这确实将其限制为返回引用的函数,因此您可以有两个函数,一个用于引用,另一个用于拥有的类型。这仍然不允许 all 类型,但允许绝对大多数类型。字符串
1.为Transformer定义一个自定义trait。使用GATified trait,你可以覆盖所有可能的类型,并且你也可以通过为拥有的函数实现trait来为大多数情况提供闭包语法的便利,但是你必须使用不太方便的显式结构和trait实现其他所有内容:
型
是的,很长。这就是劣势
deikduxw2#
如果你的闭包只返回引用,你可以显式地指定闭包返回类型为引用(及其生命周期),并使用更高排名的trait绑定:
字符串
但是如果你不能保证这一点,你唯一的选择就是使用带有泛型关联类型(GAT)的自定义trait:
型
这种方法的缺点是,您不能再使用闭包,必须手动创建一个新的结构体,该结构体为您可能需要进行的每个可能的转换实现
Transform
。对于闭包返回的类型 * 不是 * 引用的情况(即
F: for<'a> FnOnce(&'a T) -> T2<'a>
)在没有自定义trait方法的情况下将无法工作,因为Rust还不支持更高类型的概念。