Generics beginner question


#1
struct Demo<T> {
    value : [T]
}

impl<T> Demo<T>{
    pub fn new(capacity: u32) -> Self{
        Demo{
            value : [T;capacity]
        }
    }
}

Is it not possible to initialise the value array in the new function?


#2

This has numerous problems.

First: [T] is a dynamically sized type, meaning that the compiler cannot possibly know how much space it takes. This, in turn, makes it impossible to have values of this type (except behind pointers). Values of DSTs are also borderline impossible to construct in safe code. As such, Demo is also a DST, and you cannot possibly construct and return a Demo. To put it in other words, you can’t possibly make new work.

Second: Even if you could create a Demo<T>, this isn’t how you’d do it. You’re trying to store a fixed-size array in a dynamically sized array slot. They’re different types, and although pointers will coerce in some circumstances, values don’t.

Third: [T; capacity] is, in this context, meaningless for two different reasons. First, T is a type; it’d be like saying let x = i32; in that it just doesn’t make any sense. Second, capacity is a runtime value, and array sizes must be static. You cannot construct an array of some runtime-only size.

Fourth: even if you could solve all of the above, you still have the fundamental problem that you can’t create an arbitrary T from nowhere. You need to place some kind of constraint on T so that you can, or take another argument which in some way allows you to create Ts.

Fifth: u32 is probably inappropriate here; because you’re talking about a collection size, you probably want usize.

What you probably want to write is this:

struct Demo<T> {
    value: Vec<T>,
}

impl<T> Demo<T> {
    pub fn new() -> Self {
        Demo {
            value: vec![],
        }
    }
    
    pub fn with_n<F>(n: usize, mut f: F) -> Self
    where F: FnMut() -> T {
        let mut v = Vec::with_capacity(n);
        for _ in 0..n {
            v.push(f());
        }
        Demo {
            value: v,
        }
    }

    pub fn with_capacity(capacity: usize) -> Self {
        Demo {
            value: Vec::with_capacity(capacity),
        }
    }
}

fn main() {}

The fact is that doing this yourself is an advanced topic, so you’re almost certainly better off just using Vec.


#3

@DanielKeep Thank you so much. I am trying this now.
What is the purpose of with_n function, just to initialise the vector?


#4

Yes, to initialise it with n values, produced by the closure. e.g. Demo::with_n(10, || String::from("some expensive, non-copyable value")).

I should also note that with_capacity just reserves backing space; Vec::with_capacity(10).len() is still 0.