我想写一个简单的记忆功能。
fn memoize<K: Eq + Hash, V: Clone>(mut func: impl FnMut(&K) -> V) -> impl FnMut(K) -> V {
let mut cache = HashMap::new();
|key| {
cache
.entry(key)
.or_insert_with_key(|k| func(k))
.clone()
}
}
Run Code Online (Sandbox Code Playgroud)
Eq + Hash对参数的约束似乎是合理的,但Clone对返回值的约束似乎是不必要的。理想情况下的签名是:
fn memoize<K: Eq + Hash, V>(mut func: impl FnMut(&K) -> V) -> impl FnMut(K) -> &mut V
Run Code Online (Sandbox Code Playgroud)
这需要指定返回引用的生命周期(有意义)。
理想情况&mut V下,只要函数的引用存在,它就应该存在(或者类似的东西&'a mut impl FnMut(K) -> &'a mut V:)
由于 Fn 特征的 impls 不稳定,如果我想留在 Fn 特征内(而不是用 some 编写我的结构fn call(&'a mut self, key: K) -> &'a mut V),有没有办法在稳定的 Rust 中做到这一点?
不幸的是,这是不可能的。即使手动实现也是FnMut不可能的,除非我们创建Output一个通用关联类型:
pub trait GatifiedFnMut<Args: std::marker::Tuple> {
type Output<'a>
where
Self: 'a;
extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output<'_>;
}
pub struct Memoized<K, V, F> {
cache: HashMap<K, V>,
func: F,
}
impl<K: Eq + Hash, V, F: FnMut(&K) -> V> GatifiedFnMut<(K,)> for Memoized<K, V, F> {
type Output<'a> = &'a mut V
where
Self: 'a;
extern "rust-call" fn call_mut(&mut self, (key,): (K,)) -> Self::Output<'_> {
self.cache.entry(key).or_insert_with_key(&mut self.func)
}
}
Run Code Online (Sandbox Code Playgroud)
(注意:即使没有 GAT,也可以解决这个问题,但不能使用当前的FnOnce//特征,或者至少非常不舒服,请参阅下面链接的问题)FnMut。Fn
更多信息可以在 users.rust-lang.org 找到。