2017-05-02 3 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 이후입니다

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!() 
    } 
} 

호출하지 않으며,이 가능해야한다처럼 Associated Types에서 보인다 않을 것이다. 그러나 관련 유형이 허용되지 않는 오류로 인해 실패합니다.

Rust Compiler Error IndexFn(T) -> R이라는 문구를 언급하고 있으며, Fn<(T,), Output=U>이 작동해야한다고 말하지만, 야간 녹 작성기를 사용하고 있어도 작동하지는 못합니다.

컴파일 타임에 가능한 한 많은 오류를 포착하는 것이 바람직하므로 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'은 컴파일러에게 'arg'가 실제로 함수에 대한 다중 인수이고 각 튜플 값이 별도의 인수가되도록 변환을 수행하는 것입니다. 아마도 함수에 전달 된 하나의 큰 튜플은 하드웨어 수준에서 많은 개별 값과 다르게 동작합니다. – Shepmaster

관련 문제