是否有可能避免对"FnOnce"的无意义定义?

Mat*_*ahl 1 overloading rust

在尝试重载调用语法时,我引入了一个简单的缓存,可以缓存昂贵的计算结果.我对使用一段语法感到有点困惑.我将在问题之前逐步介绍代码.

缓存旨在使用如下:

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));
    }
}
Run Code Online (Sandbox Code Playgroud)

我们首先使用序言来启用尚未稳定的功能:

#![feature(fn_traits, unboxed_closures)]

use std::collections::HashMap;
use std::hash::Hash;
Run Code Online (Sandbox Code Playgroud)

我们将缓存作为具有a 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)
    }
}
Run Code Online (Sandbox Code Playgroud)

我创建了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)
    }
}
Run Code Online (Sandbox Code Playgroud)

即使我发现语法FnMut<(T,)>很奇怪,这很好而且安全,并且很清楚地表达了意图.由于我需要定义函数的返回类型,我想将开头写为:

impl<T, R> FnMut<(T,), Output=R> for Cache<T, R>
    where T: Eq + Hash + Copy,
          R: Copy
{}
Run Code Online (Sandbox Code Playgroud)

但是失败了,出现了错误:

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
Run Code Online (Sandbox Code Playgroud)

我必须这样实现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!()
    }
}
Run Code Online (Sandbox Code Playgroud)

这是毫无意义的,因为call_once永远不会被调用,并且从关联类型看起来这应该是可能的.但是,它失败并出现错误,那里不允许关联类型.

锈编译器错误索引提到的语法Fn(T) -> R和也说,Fn<(T,), Output=U>应该工作,但我不能让它即使我使用夜间锈编译工作.

由于希望在编译时捕获尽可能多的错误,因此最好避免创建"未实现"的函数,FnOnce因为它将在运行时而不是编译时失败.

是否可以仅实现FnMut并以某种方式提供函数的返回类型?

She*_*ter 5

这是毫无意义的,因为call_once永远不会被称为

这不取决于你决定; 这取决于来电者.他们可能决定在FnOnce上下文中调用缓存.

好消息是有一个非常合理的实现FnOnce- 只需委托FnMut实现:

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(mut self, arg: (T,))
        -> Self::Output
    {
        self.call_mut(arg)
    }
}
Run Code Online (Sandbox Code Playgroud)

这就是编译器自动实现这些特性的功能; 它也代表FnMutFn是否合适.

也可以看看