Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Explicit type annotation for generic constructor of a generic type

I'm writing a Rust binding for a C library. It implements an entity that can be constructed from different source entities, possibly saving some references internally. I want the Rust type to enforce a safe ownership policy, thus the wrapper struct is generic, parameterized by the type of the stored reference.

struct Foobar<T> {
    origin: T,
}

Then I implement some constructors for my Foobar type.

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 }
    }
}

And here comes the problem: both the struct and the constructor are independently parameterized. While the constructor type parameter can be inferred from its arguments, the struct type parameter is not used in the constructor and cannot be inferred. Thus, the naive way to call a constructor

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

confuses 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();

It can be fixed by providing some arbitrary type parameter:

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

...which is all sorts of ugly. Another way to solve the problem would be to turn constructors into free functions, though it would be (kinda) non-idiomatic.

The question is, am I missing something? The design seems to be flawed in some way. What would be the proper way to design this type to get away with only one level of generics?


Minimal reproducible example on Rust playpen

For reference, my compiler version is:

$ rustc --version
rustc 1.1.0-dev (built 2015-04-26)
like image 690
Mikhail Borisov Avatar asked May 03 '15 20:05

Mikhail Borisov


1 Answers

As I understand it, your original code is parameterized on T, but you have methods that want to specify the parameter. The trick is to not have a generic for these cases. Instead, try creating specialized implementations for each interesting type:

// 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);
}
like image 161
Shepmaster Avatar answered Sep 20 '22 01:09

Shepmaster