【发布时间】:2017-05-02 20:10:09
【问题描述】:
在尝试重载调用语法时,我引入了一个简单的缓存,可以缓存昂贵计算的结果。我对使用一段语法有点困惑。我会在问题之前一步一步介绍代码。
缓存打算这样使用:
fn fib(x: i32) -> i32 {
if x < 2 { x } else { fib(x-1) + fib(x-2) }
}
fn main() {
let mut cfib = Cache::new(fib);
// Loop that repeats computation and extracts it from the cache
// the second time.
for x in 1..200 {
let val = 5 * x % 40;
println!("fibc({}) = {}", val, cfib(val));
}
}
我们首先要启用尚未稳定的功能:
#![feature(fn_traits, unboxed_closures)]
use std::collections::HashMap;
use std::hash::Hash;
我们将缓存引入为具有HashMap 和计算新值的函数的结构。
struct Cache<T, R> {
cache: HashMap<T, R>,
func: fn(T) -> R,
}
impl<T, R> Cache<T, R>
where T: Eq + Hash + Copy,
R: Copy
{
fn new(func: fn(T) -> R) -> Cache<T, R> {
Cache { cache: HashMap::new(), func: func }
}
fn compute(&mut self, x: T) -> R {
let func = self.func;
let do_insert = || (func)(x);
*self.cache.entry(x).or_insert_with(do_insert)
}
}
我创建了一个 FnMut 特征的实现,因为缓存需要是可变的。
impl<T, R> FnMut<(T,)> for Cache<T, R>
where T: Eq + Hash + Copy,
R: Copy
{
extern "rust-call" fn call_mut(&mut self, args: (T,))
-> Self::Output
{
let (arg,) = args;
self.compute(arg)
}
}
尽管我觉得FnMut<(T,)> 的语法很奇怪,但这很好,很安全,并且表达的意图很清楚。由于我需要定义函数的返回类型,所以我想把开头写成:
impl<T, R> FnMut<(T,), Output=R> for Cache<T, R>
where T: Eq + Hash + Copy,
R: Copy
{}
但是失败并出现错误:
error[E0229]: associated type bindings are not allowed here
--> src/main.rs:55:24
|
55 | impl<T, R> FnMut<(T,), Output=R> for Cache<T, R>
| ^^^^^^^^ associate type not allowed here
我必须像这样实现FnOnce:
impl<T, R> FnOnce<(T,)> for Cache<T,R>
where T: Eq + Hash + Copy,
R: Copy
{
type Output = R;
extern "rust-call" fn call_once(self, _arg: (T,))
-> Self::Output
{
unimplemented!()
}
}
这是没有意义的,因为call_once 永远不会被调用,而从Associated Types 看来这应该是可能的。但是,它会失败,并出现一个错误,即此处不允许关联类型。
Rust Compiler Error Index 提到语法 Fn(T) -> R 并且还说 Fn<(T,), Output=U> 应该可以工作,但即使我使用的是夜间 Rust 编译器,我也无法使其工作。
由于希望在编译时捕获尽可能多的错误,因此最好避免在 FnOnce 中创建“未实现”函数,因为这将在运行时而不是编译时失败。
是否可以只实现FnMut并以某种方式提供函数的返回类型?
【问题讨论】:
标签: rust overloading