2015-05-03 24 views
5

Piszę wiązanie Rust dla biblioteki C. Implementuje encję, która może być zbudowana z różnych elementów źródłowych, prawdopodobnie zapisując wewnętrznie pewne referencje. Chcę, aby typ rdzy wymuszał bezpieczną politykę praw własności, w związku z czym struktura opakowania jest generyczna, sparametryzowana przez typ przechowywanego odwołania.Opis typu jawnego dla generycznego konstruktora generycznego typu

struct Foobar<T> { 
    origin: T, 
} 

Następnie implementuję niektóre konstruktory dla mojego typu Foobar.

impl<T> Foobar<T> { 
    fn from_nowhere() -> Foobar<()> { 
     Foobar { origin:() } 
    } 

    fn from_orange<F>(orange: &mut F) -> Foobar<&mut F> 
     where F: Orange 
    { 
     Foobar { origin: orange } 
    } 

    fn from_callback<F>(callback: F) -> Foobar<F> 
     where F: FnMut(u64) -> u64 
    { 
     Foobar { origin: callback } 
    } 
} 

I tu pojawia się problem: zarówno struct, jak i konstruktor są niezależnie sparametryzowane. Podczas gdy parametr typu konstruktora można wywnioskować z jego argumentów, parametr typu struct nie jest używany w konstruktorze i nie można go wywnioskować. Tak więc, naiwny sposób wywoływania konstruktora

let a = Foobar::from_nowhere(); 
let b = Foobar::from_orange(&mut fruit); 
let c = Foobar::from_callback(|x| x*x); 

myli rustc:

rustgen.rs:43:13: 43:33 error: unable to infer enough type information about `_`; type annotations required [E0282] 
    rustgen.rs:43  let a = Foobar::from_nowhere(); 

To może być ustalona poprzez zapewnienie jakiś dowolny parametr typu:

let a = Foobar::<()>::from_nowhere(); 
let b = Foobar::<()>::from_orange(&mut fruit); 
let c = Foobar::<()>::from_callback(|x| x*x); 

... co jest wszystko rodzaje brzydkie. Innym sposobem rozwiązania problemu byłoby przekształcenie konstruktorów w wolne funkcje, choć byłoby to (rodzaj) nie-idiomatyczne.

Pytanie brzmi: czy czegoś brakuje? Wygląda na to, że projekt jest wadliwy. Jaki byłby właściwy sposób zaprojektowania tego typu, aby uciec tylko z jednym poziomem generycznych?


Minimal reproducible example on Rust playpen

Dla porównania, moja wersja kompilatora jest:

$ rustc --version 
rustc 1.1.0-dev (built 2015-04-26) 

Odpowiedz

7

Jak rozumiem, oryginalny kod jest parametryzowane na T, ale trzeba metod, które chcą, aby określić parametr . Sztuczka polega na tym, że nie ma generycznego dla tych przypadków. Zamiast tego spróbuj utworzyć specjalistyczne implementacje dla każdego interesującego typu:

// this is just an example. suppress unrelated warnings 
#![allow(dead_code, unused_variables)] 

struct Foobar<T> { 
    origin: T, 
} 

trait Orange {} 

struct Grapefruit; 

impl Orange for Grapefruit {} 

impl Foobar<()> { 
    fn from_nowhere() -> Foobar<()> { 
     Foobar { origin:() } 
    } 
} 

impl<'a, F> Foobar<&'a mut F> 
    where F: Orange 
{ 
    fn from_orange(orange: &'a mut F) -> Foobar<&'a mut F> { 
     Foobar { origin: orange } 
    } 
} 

impl<F> Foobar<F> 
    where F: FnMut(u64) -> u64 
{ 
    fn from_callback(callback: F) -> Foobar<F> { 
     Foobar { origin: callback } 
    } 
} 

fn main() { 
    let mut fruit = Grapefruit; 

    // What I actually wanted to do 
    let a1 = Foobar::from_nowhere(); 
    let b1 = Foobar::from_orange(&mut fruit); 
    let c1 = Foobar::from_callback(|x| x*x); 
} 
+1

Dziękujemy! Nie zdawałem sobie sprawy, że możliwe jest posiadanie wielu klauzul "impl" dla różnych parametryzacji. –