Initialize a struct with generic fields with defaults other than 0

Hi,

I'm trying to implement the initialization of a struct with generic fields, where one field is initialized with a user-defined value and the other always defaults to a 1 depending on the generic type of the rest of the fields, just as it works with Default::default() for the value 0:

struct Vector<T>{
    x: T,
    w: T,
}

struct Point<T>{
    x: T,
    w: T,
}

impl<T> Vector<T> {
    fn new(x: T) -> Self where T: Default {
        Self {x, w: Default::default() }    // <= returns 0i64, 0f64, etc.
    }
}

impl<T> Point<T>{
    fn new(x: T) -> Self where T: Default {
        Self {x, w: /* how to return a 1i64, 1f64, etc. ?*/ }
    }    
}

So that when called:

let vector_i = Vector::new(2i64);
let vector_f = Vector::new(3.5f64)
let point_i = Point::new(2i64);
let point_f = Point::new(3.5f64);

Initialize as follows:

// works correctly thanks to Default::default() returning 0 or 0.0 automatically...
vector_i Vector { x: 2, w: 0}  
vector_f Vector {x: 3.5, w: 0.0}

// how to make it work?
point_i: Point { x: 2, w: 1 }
point_f: Point { x: 3.5 w: 1.0 }

Is there a way to make it work without separate implementation blocks like impl Point<f64> or impl Point<64> so that it is implemented in a single generic impl block just like with Vector?

Thanks in advance for any help.

No, but people have already written such trait for you.

2 Likes

Thank you very much, works precisely as needed:

use num::One;

#[derive(Debug)]
struct Point<T> {
    x: T,
    w: T,
}

impl<T> Point<T>{
    fn new(x: T) -> Self where T: One{
        Self {x, w: One::one() }
    }

}

fn main() {

    let point_i = Point::new(2i64);
    let point_f = Point::new(2f64);
    println!("pi: {:?}\npf: {:?}", point_i, point_f);
}

The output:

pi: Point { x: 2, w: 1 }
pf: Point { x: 2.0, w: 1.0 }

To elaborate a bit, your original code isn’t restricted to numeric types. So, you need to either come up with an implementation that works for arbitrary types (T = HashMap<String,u32>>, for example) or somehow disallows them via a trait bound.

Thanks and in agreement. I believe that restricting via trait bound by leveraging the num::Num trait is the most straightforward way:

use num::{One, Num};

impl<T> Point<T>{
    fn new(x: T) -> Self where T: One + Num {
        Self {x, w: One::one() }
    }

}

Note that One is a supertrait of Num, so if you use Num anyway, you don't have to explicitly ask for One - it will be required (and available) automatically.

1 Like

Thanks for the note, appreciated.