Strange behavior default generic parameter type

This thread, and I think there was one other inbetween. Documentation on where they work, where they don't, and how it relates to inference would be a nice start -- at least, I failed to find any. There's a couple links in those comments; this issues about on how RFC 213 stalled out I think is the main one highlighting shortfalls. There are other issues linked in those discussions in turn (e.g. a documentation request and another one, this bug, and so on).

1 Like

I was playing around with variations and came up with two more I don't think have been mentioned yet:

#[derive(Debug, Default)]
struct StructName<A = u32, B = String> {
    field_a: A,
    field_b: B,
}

fn main() {
    let a = StructName::<_> {
        field_a: String::from("Spring"),
        ..StructName::default()
    };

    println!("{:?}", a);
}

and

#[derive(Debug, Default)]
struct StructName<A = u32, B = String> {
    field_a: A,
    field_b: B,
}

fn main() {
    let a = StructName {
        field_a: String::from("Spring"),
        ..StructName::<_>::default()
    };

    println!("{:?}", a);
}

This one looks a bit more readable to me:

#[derive(Debug, Default)]
struct StructName<A = u32, B = String> {
    field_a: A,
    field_b: B,
}

fn main() {
    let a = StructName::<_> {
        field_a: String::from("Spring"),
        ..Default::default()
    };

    println!("{:?}", a);
}

Unfortunately, this is just a special case, the same as the one I described above. We have to write the required number of underscores. This is not a general solution. This will not work in many other cases, for example:

#[derive(Debug, Default)]
struct StructName<A = u32, B = String, C = bool> {
    field_a: A,
    field_b: B,
    field_c: C,
}

fn main() {
    let a = StructName::<_> {
        field_a: String::from("Spring"),
        field_b: true,
        ..<_>::default()
    };

    println!("{:?}", a);
}

Error:

mismatched types
  --> src/main.rs:12:18
   |
12 |         field_b: true,
   |                  ^^^^
   |                  |
   |                  expected struct `String`, found `bool`
   |                  help: try using a conversion method: `true.to_string()`

The reason for this behavior is as follows:

We use the struct update syntax:

#[derive(Debug)]
struct StructName{
    field_a: u32,
    field_b: String,
    field_c: bool,
}

fn main() {
    // Type of this struct: `StructName<u32, String, bool>`
    let defaulted_struct = StructName { 
        field_a: 42,
        field_b: String::from("Spring"),
        field_c: true,
    };
    
    // Type of this struct: `StructName<u32, String, bool>`
    let a = StructName {
        field_a: 2,
        field_b: String::from("Summer"),
        ..defaulted_struct
    };  

    println!("{:?}", a);
}

It's okay, because the types of structures are the same.

Instead of defaulted_struct, we can use a method Default::default() that will return a concrete structure with concrete types.

This example works (this is just a special case with comments, not a general solution):

#[derive(Debug, Default)]
struct StructName<A = u32, B = String, C = bool> {
    field_a: A,
    field_b: B,
    field_c: C,
}

fn main() {
    
    // Type of this struct: `StructName<u32, String, bool>`   
    let defaulted_struct = StructName {
        ..Default::default()
    };

    
    /*
    // Commented code below will result in an error 
    // because `defaulted_struct` has type `StructName<u32, String, bool>
    //
    // Type of this struct: `StructName<String, bool, _>`  
    let a = StructName {
        field_a: String::from("Spring"),
        field_b: true,
        ..defaulted_struct 
    };
    */
        
    // This is a different structure because their types are different
    // We cannot use the struct update syntax 
    // for structures of different types. 
    
    // For this to be possible, we must use StructName::<_, _>
    // But, this is very inconvenient, because 
    // the number of underscores must correspond
    // to the number of filled fields. 
    
    // It works: 
    
    // `defaulted_struct` type: `StructName<u32,    String, bool>`     
    //         new struct type: `StructName<String, bool,   _>`
    let a = StructName::<_, _> {  // we ignore (I think so) the first 
                                  // two types of `defaulted_struct` 
                                  // with `StructName::<_, _>` 
        field_a: String::from("Spring"),
        field_b: true,
        ..defaulted_struct
    };

    println!("{:?}", a);
}

I am thinking of implementing default types for generics with trait bounds for a finite number of listed types (since negative bounds do not exist), and procedural macros to generate this.

1 Like

You could do:

#[derive(Debug, Default)]
struct StructName<A = u32, B = String, C = bool> {
    field_a: A,
    field_b: B,
    field_c: C,
}

fn main() {
    let a = StructName::<_,_> {
        field_a: String::from("Spring"),
        field_b: true,
        ..Default::default()
    };

    println!("{:?}", a);
}

Sure. I wrote this as an example to make it clearer how it works.

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.