背景
Rust中的闭包具有匿名类型,不能称为已知的具体类型。但是from Feb 2017,所有非捕获闭包都可以转换为匿名函数,并且与函数指针具有相同的类型。
我想创建一个标准化curried闭包的类型。如果我们有一个函数fn(T1, T2, ...) -> R
,我们可以使用FnXXX(T2, ...) -> R
的特殊类型(将XXX
替换为或
Mut
或Once
,或者Box
)。然后可以在容器内使用它们而不涉及动态调度。
尝试FnOnce
以下作品:
#![feature(unboxed_closures)]
#![feature(fn_traits)]
struct Curry0<T, R> {
f: fn(T) -> R,
v: T,
}
impl<T, R> FnOnce<()> for Curry0<T, R> {
type Output = R;
extern "rust-call" fn call_once(self, _: ()) -> R {
(self.f)(self.v)
}
}
fn curry<T, R>(f: fn(T) -> R, v: T) -> impl FnOnce() -> R {
Curry0 { f: f, v: v }
}
fn main() {
curry(|s| println!("{}", s), "Hello, World!")()
}
但是,我无法添加以下内容:
impl<'a, T, R> FnMut<()> for Curry0<&'a mut T, R> {
extern "rust-call" fn call_mut(&mut self, _: ()) -> R {
(self.f)(self.v)
}
}
错误消息:
error[E0312]: lifetime of reference outlives lifetime of borrowed content...
--> src/main.rs:16:18
|
16 | (self.f)(self.v)
| ^^^^^^
|
note: ...the reference is valid for the lifetime 'a as defined on the impl at 14:1...
--> src/main.rs:14:1
|
14 | impl<'a, T, R> FnMut<()> for Curry0<&'a mut T, R> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...but the borrowed content is only valid for the anonymous lifetime #1 defined on the method body at 15:5
--> src/main.rs:15:5
|
15 | / extern "rust-call" fn call_mut(&mut self, _: ()) -> R {
16 | | (self.f)(self.v)
17 | | }
| |_____^
我的理解是,为了实现这一目标,call_mut
必须注意&mut self
仅适用于函数本身,因此如果f
返回引用v
的内容在里面,它将是无效的。
尝试FnMut
要使用FnMut
而不是FnOnce
,我写道:
#![feature(unboxed_closures)]
#![feature(fn_traits)]
struct Curry0Mut<'a, 'b, T, R>
where
T: 'a,
R: 'b,
{
f: fn(&mut T) -> R,
v: &'a mut T,
_l: std::marker::PhantomData<&'b ()>,
}
impl<'a, 'b, T, R> FnOnce<()> for Curry0Mut<'a, 'b, T, R> {
type Output = R;
extern "rust-call" fn call_once(self, _: ()) -> R {
(self.f)(self.v)
}
}
impl<'a, 'b, T, R> FnMut<()> for Curry0Mut<'a, 'b, T, R>
where
T: 'a,
R: 'b,
{
extern "rust-call" fn call_mut(&mut self, _: ()) -> R {
(self.f)(self.v)
}
}
fn curry<'a, T, R>(f: fn(&mut T) -> R, v: &'a mut T) -> impl FnMut() -> R + 'a {
Curry0Mut {
f: f,
v: v,
_l: std::marker::PhantomData,
}
}
fn main() {
let mut v = "Hello, World".to_owned();
curry(|s| println!("{}", s), &mut v)();
}
这更复杂,遗憾的是我们有两种结构,只是略有不同的用法。当我仔细观察我必须在这里进行的更改时,我发现f
实际上是其参数生命周期的通用函数,而T
的生命周期与此无关。我们还必须确保R
的寿命不比闭包本身长,所以它的生命周期必须在闭包内编码。
我不会详细介绍Fn
,因为它类似。只是要注意这会使情况变得更糟,因为我们需要Curry0
的另一种变体,因为&mut T
不是&T
。
问题
是否有可能表达v
的生命期望值与f
s参数不同的事实?
例如,如何编写如下内容:
struct Curry0<'a, 'b, T, R>
where
R: 'b,
{
f: fn(T) -> R, //T have generic lifetime
v: T, //T: 'a
_a: std::marker::PhantomData<&'a ()>,
_b: std::marker::PhantomData<&'b ()>,
}