the8472
the8472

Reputation: 43052

Create a generic struct with Option<T> without specifying T when instantiating with None

I have a

struct Foo<T>
where
    T: // ... some complex trait bound ...
{
    a: Bar,
    b: Option<T>,
}

When attempting to instantiate the struct with a b: None the compiler complains that it cannot infer the type and requires a type hint e.g. via the turbofish syntax. That is onerous on the caller because they will have to find a type that fulfills the trait bounds and import it despite not caring about that optional functionality.

I think what I am looking for would be a bottom type that automatically fulfills any trait bounds but cannot be instantiated so that None::<Bottom> could be used, but I have not found such a type in the documentation.

Upvotes: 4

Views: 5494

Answers (2)

Chris Emerson
Chris Emerson

Reputation: 14041

One option to avoid the need to the turbofish operator is to have a type alias:

trait MyTrait {}
impl MyTrait for () {}

struct Foo<T: MyTrait> {
    i: isize,
    o: Option<T>,
}

type Bar = Foo<()>;

fn main() {
    let foo_default = Bar { i: 1, o: None };
}

I used () as the default for simplicity, but ! (when available) or your own bottom type as in @Shepmaster's answer may be better.

A constructor function could also work if you don't mind Foo::new_default(i) or similar.

Upvotes: 2

Shepmaster
Shepmaster

Reputation: 430851

There's a feature in the works that allows specifying the never type as !. This is not present in stable Rust, so you need to use a nightly and a feature flag:

#![feature(never_type)]

fn thing<T>() -> Option<T> {
    None
}

fn main() {
    thing::<!>();
}

However, this doesn't work for your case yet (this is part of the reason that it's unstable):

#![feature(never_type)]

trait NothingImplementsMe {}

fn thing<T>() -> Option<T> 
    where T: NothingImplementsMe,
{
    None
}

fn main() {
    thing::<!>();
}
error[E0277]: the trait bound `!: NothingImplementsMe` is not satisfied
  --> src/main.rs:12:5
   |
12 |     thing::<!>();
   |     ^^^^^^^^^^ the trait `NothingImplementsMe` is not implemented for `!`
   |
   = note: required by `thing`

The very first unresolved question on the tracking issue is:

What traits should we implement for !?


Since this feature is both unstable and doesn't do what you want, you may want to consider creating your own bespoke "bottom" type:

trait AlmostNothingImplementsMe {
    fn foo();
}

struct Nope;
impl AlmostNothingImplementsMe for Nope {
    fn foo() { unimplemented!() }
}

fn thing<T>() -> Option<T> 
    where T: AlmostNothingImplementsMe,
{
    None
}

fn main() {
    thing::<Nope>();
}

To improve the UX of this, I'd suggest creating a builder of some type that starts you off with the faux-bottom type:

mod nested {
    pub trait AlmostNothingImplementsMe {
        fn foo();
    }

    pub struct Nope;
    impl AlmostNothingImplementsMe for Nope {
        fn foo() { unimplemented!() }
    }

    pub fn with_value<T>(t: T) -> Option<T> 
        where T: AlmostNothingImplementsMe,
    {
        Some(t)
    }

    pub fn without_value() -> Option<Nope> {
        None
    }
}

fn main() {
    nested::without_value();
}

You can see this similar pattern in crates like Hyper, although it boxes the concrete type so you don't see it from the outside.

Upvotes: 4

Related Questions