Where might a Default Generic Type be used in a plain struct?

I have read the Rust docs which give an example of generic type defaults being used in a trait (specifically in overloading the Add operator where the default is Self).

Is there a meaningful use case where the type default is applied to a plain old struct and impl block?

struct Foo<T=DefaultType> {...}

Sure, for example HashMap<K, V, S = RandomState>.

3 Likes

The allocator on many standard collections is another example.

2 Likes

In general, default parameters (be them generic or function parameters) are a nice way too to add additional parameters to an existing API without needing to rewrite all usages of the API to include the new parameter.

They're also good if you think the common user will always use the defaults and you want to simplify the interface while maintaining the ability to select a non-default option for more advanced use cases.
The HashMap example @cuviper cited is a good instance of this.

2 Likes

There's also the really neat use of a default parameter in anyhow::Result - while this is a type alias for the standard enum Result, it adds a default type parameter saying that "unless you say otherwise, the error type is anyhow::Error.

This means that I can write code like the following, and it compiles correctly:

use anyhow::Result;
use core::num::ParseIntError;

pub fn decode_octal(octal: &str) -> Result<u32, ParseIntError> {
    u32::from_str_radix(octal, 8)
}

fn throw_party() -> Result<()> {
    todo!("work out how to throw a great party");
}

pub fn party_time(octal: &str) -> Result<u32> {
    let party = decode_octal(octal)?;
    for _ in 0..party {
        throw_party()?;
    }
    Ok(party.saturating_sub(1))
}
3 Likes

Thank you for the reply. So, decode_octal() and throw_party() both return errors of some type, but then the '?' operator converts those errors to the anyhow default 'Error' and stuffs that into the Result returned from party_time.

Did I understand that correctly?

Thanks for these examples @quinedot @cuviper and others- makes sense.

1 Like

The trick is not in the conversion, which you've understood correctly, but in the fact that I have two different ways of putting together my Result<T, E>.

In decode_octal' return type, I specify both T and E - the success type is a u32, the failure type is a core::num::ParseIntError.

In throw_party and party_time return types, however, I just specify T; for throw_party, it's (), for party_time, it's u32. Because anyhow::Result has a default generic type of E = anyhow::Error, this works - Rust knows that I want the failure type to be anyhow::Error.

If you try to do this without a default generic type, you get stuck. anyhow::Result needs to specify the error type to let throw_party and party_time type signatures make sense, but it also needs it to be generic so that decode_octal can specify that its E is not anyhow::Error, but core::num::ParseIntError. The only way to do this is with a default generic type, so that I can override it at use point.

1 Like

Got it - thanks !

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.