2017-05-02 10 views
1

呼び出し構文のオーバーロードを試す際に、高価な計算結果をキャッシュできるシンプルなキャッシュを導入しました。私は構文の一部の使用について少し混乱しています。質問の前にステップごとにコードを紹介します。`FnOnce`の無意味な定義を避けることはできますか?

キャッシュは次のように使用することが意図されています

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を実装する必要がありました:

call_onceが呼び出されることはありませんし、これが可能でなければなりませんように Associated Typesからそれはそうので、一種の無意味です
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!() 
    } 
} 

。ただし、関連付けられた型が許可されていないというエラーで失敗します。

Rust Compiler Error IndexにはFn(T) -> Rの文法が記載されていますが、Fn<(T,), Output=U>も動作するはずですが、夜間のRustコンパイラを使用していても動作させることはできません。

コンパイル時にできるだけ多くのエラーをキャッチすることが望ましいので、FnOnceに「実装されていない」関数を作成しないでください。コンパイル時ではなく実行時にエラーが発生するためです。

FnMutだけを実装し、何らかの形で関数の戻り値の型を指定することはできますか?

答えて

5

Which is kind of pointless since call_once will never be called

これはあなたの判断に任されません。それはの発信者までです。彼らは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) 
    } 
} 

これは、これらの形質のコンパイラの自動実装が何をするかです。必要に応じてFnMutFnに委譲します。

は、動作しているよう

+0

も参照してください。ありがとう。 –

+0

'extern 'rust-callはここで厳密に必要ですか? –

+0

@MatthieuM。うんこの特性は「extern」rust-call関数を定義しており、実装はその特性定義と一致しなければなりません。より広義には、 'rust-call'は、' arg'が実際に関数の複数の引数であり、各タプル値が別の引数であるような変換を行うことをコンパイラに伝えるものです。おそらく、関数に渡される単一の大きなタプルは、ハードウェアレベルでは多くの別々の値とは異なる動作をするでしょう。 – Shepmaster

関連する問題