Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use parentheses for generics?

I'm getting the compile error:

angle-bracket notation is not stable when used with the Fn family of traits, use parentheses [E0215]

What does this mean? How do I "use parentheses"?

use std::hash::Hash;
use std::collections::HashMap;

struct MemoedFun<A, R> {
    fun: fn(&A) -> R,
    map: HashMap<A, R>,
}

fn memoize<A: Eq + Hash, R>(fun: fn(&A) -> R) -> MemoedFun<A, R> {
    MemoedFun {
        fun: fun,
        map: HashMap::new(),
    }
}

impl<'a, A, R> FnOnce<A> for &'a MemoedFun<A, R> {
    type Output=&'a R;
}
like image 426
dspyz Avatar asked Jul 11 '15 06:07

dspyz


1 Answers

There are several problems with your code.

First of all, you cannot use Fn* traits directly in stable Rust. This includes 1) using angle-brackets notation, and 2) implementing these traits. It is possible to enable a feature flag for both of these things in unstable Rust though.

Second, if you do use angle brackets for closure traits, you have to use tuples for arguments, even if there is only one argument:

FnOnce<(A,)>

Third, the error message is that instead of FnOnce<(T, U), Output=V> you should write FnOnce(T, U) -> V. That's what is meant under "using parentheses" message. I agree that this message is misleading here because you can't implement Fn when it is written like this because of associated types. I guess the error about implementing Fn types should take precedence over this error.

Fourth, you won't be able to do what you want (a memoizing function backed by a hash map) when you use &'a MemoedFun<A, R> because you need a mutable pointer to update the map. You need to implement FnOnce for &'a mut MemoedFun<A, R>:

impl<'a, A: Eq + Hash, R> FnOnce<(A,)> for &'a mut MemoedFun<A, R> {
    type Output = &'a R;

    extern "rust-call" fn call_once(self, (arg,): (A,)) -> &'a R {
        if self.map.contains_key(&arg) {
            &self.map[&arg]
        } else {
            let r = (self.fun)(&arg);
            self.map.entry(arg).or_insert(r)
        }
    }
}

And finally, the resulting code you would have to write to use this memoizer isn't pretty. You can't use function syntax on your "function" for some reason, so you would need to use call_once() directly:

fn computer(x: &i32) -> i32 {
    println!("Computing for {}", x);
    -*x
}

let mut f = memoize(computer);

println!("f(10): {}", (&mut f).call_once((10,)));
println!("f(10): {}", (&mut f).call_once((10,)));
println!("f(42): {}", (&mut f).call_once((42,)));

(try it here)

There is a reason why Fn* traits manual implementation is not stabilized, after all.

like image 100
Vladimir Matveev Avatar answered Sep 22 '22 12:09

Vladimir Matveev