Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
867 views
in Technique[技术] by (71.8m points)

rust - 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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...