Need clarification about generic default types

I'm trying to understand what the use of default types (not sure if the right term) is.

struct Object<T = f64> { // What I mean by "default type"
    member: T,
}

let mut obj = Object { member: Default::default() };
obj.member = "I'm not a float"; // Works!

struct Object<T> {
    member: T,
}

struct Builder<T = f64> {
    target_value: T,
}

impl<T: Default> Builder<T> {
    pub fn new() -> Builder<T> {
        Builder {
            target_value: T::default(), // Call T's default constructor…
        }
    }

    pub fn build(self) -> Object<T> {
        Object {
            member: self.target_value,
        }
    }
}

fn main() {
    let builder = Builder::new();
    let mut object = builder.build();
    object.member = "I'm not a float"; // I expected f64 = &'static str (Error)
}

So this code compiles. I presume because Rust tracks forward to the &str assignment and concludes that T should be of that type.

If so, then could you give me examples where the default type comes into play? I tried to think of some, but it seems I always end up giving Rust a hint, either directly by type or implicitly by assignment. Or differently put: What's the effective difference between <T = ImThisType> and <T = YouMayLeaveMeUnspecified>?

1 Like

A good example is std::ops::Add - it has defaulted RHS type parameter. So whenever you don't specify it explicitly, it defaults to the Self type. So when you implement it for some type Foo, you don't need to write impl Add<Foo> for Foo. Similarly, when you use it as a generic type constraint T: Add<Output=T> you don't need to write T: Add<T, Output=T>.

1 Like

I looked at the documentation of std::ops::Add and the usage examples helped clarify it. Thanks.