4

I have the following struct:

pub struct Foo<T> {
    some_value: T,
}

impl<T> Foo<T> {
    pub fn new(value: T) -> Self {
        Self { some_value: value }
    }
}

// Implement `Default()`, assuming that the underlying stored type
// `T` also implements `Default`.
impl<T> Default for Foo<T>
where
    T: Default,
{
    fn default() -> Self {
        Self::new(T::default())
    }
}

I would like Foo::default() to be available if T implements Default, but not available otherwise.

Is it possible to specify "conditional implementation" in Rust, where we implement a trait if and only if some generic type trait constraint is satisfied? If the constraint is not satisfied, the target trait (Default in this case) is not implemented and there is no compiler error.

In other words, would is it possible to use the generic struct above in the following way?

fn main() {
    // Okay, because `u32` implements `Default`.
    let foo = Foo::<u32>::default();

    // This should produce a compiler error, because `Result` does not implement
    // the `Default` trait.
    //let bar = Foo::<Result<String, String>>::default();

    // This is okay. The `Foo<Result<...>>` specialisation does not implement
    // `Default`, but we're not attempting to use that trait here.
    let bar = Foo::<Result<u32, String>>::new(Ok(42));
}
Donald Whyte
  • 195
  • 2
  • 9
  • 1
    *Questions seeking debugging help ("why isn't this code working?") must include the desired behavior, a **specific problem or error** and the **shortest code necessary to reproduce it** in the question itself.* — This code is not even syntactically valid Rust code, thus it fails with a syntax error. Additionally, you haven't included the complete error message, so we don't know what error you might have been experiencing. – Shepmaster Dec 31 '17 at 22:03
  • @Shepmaster My apologies. In hindsight, this question wasn't very well thought out. I've amended the syntax errors and added extra clarification to the question. I've selected Kornel's post as the accepted answer, but will also edit the original question post to confirm that it is indeed possible with the code I've specified. – Donald Whyte Jan 06 '18 at 12:32
  • I've updated the question to include extra explanation of @Kornel's answer. – Donald Whyte Jan 06 '18 at 12:42

3 Answers3

6

For this specific instance, the implementation provided by derive(Default) does exactly what you've asked for:

#[derive(Default)]
pub struct Foo<T> {
    some_value: T,
}

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
3

Your example, after fixing minor syntax issues, does work:

pub struct Foo<T> {
    some_value: T,
}

impl<T> Foo<T> {
    pub fn new(value: T) -> Self {
        Self { some_value: value }
    }
}

// Implement `Default()`, assuming that the underlying stored type
// `T` also implements `Default`.
impl<T> Default for Foo<T>
where
    T: Default,
{
    fn default() -> Self {
        Self::new(T::default())
    }
}

fn main() {}

Playground

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Kornel
  • 97,764
  • 37
  • 219
  • 309
0

As pointed out by @Kornel's answer, it turns out the compiler already conditionally implements traits of generic structs.

The Default trait is only implemented for struct Foo<T> if T satisfies the type constraints specified when defining the implementation of Default. In this case, the constraints were defined as where T: Default. Therefore, Foo<T> only implements Default if T implements Default.

As shown by the fn main() example above, any attempt to use the Foo<T>'s Default implementation when T does not implement Default produces a compiler error.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Donald Whyte
  • 195
  • 2
  • 9