gpt4 book ai didi

rust - 是否可以避免 `FnOnce` 的无意义定义?

转载 作者:行者123 更新时间:2023-11-29 08:11:30 25 4
gpt4 key购买 nike

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

缓存旨在像这样使用:

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并以某种方式提供函数的返回类型?

最佳答案

Which is kind of pointless since call_once will never be called

这不是你能决定的;这取决于调用者。他们可能决定在 FnOnce 上下文中调用缓存。

好消息是 FnOnce 有一个完全合理的实现 — 只需委托(delegate)给 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)
}
}

这就是编译器自动实现这些特性所做的;如果合适,它还会将 FnMut 委托(delegate)给 Fn

另见

关于rust - 是否可以避免 `FnOnce` 的无意义定义?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43746406/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com