Constructing a &Vec<T>

  1. I have code that looks something like this:
pub enum Foo {
    A(Vec<f32>),
    B(f32, f32, f32, f32),
    C(f32, f32, f32),
    D(f32, ),
}

impl Foo {
    fn ref_to_f32(&self) -> &Vec<f32> {
        match self {
            Foo::A(v) => v,
            Foo::B(a, b, c, d) => &vec![*a, *b, *c, *d],
            Foo::C(a, b, c) => &vec![*a, *b, *c],
            Foo::D(a) => &vec![*a],
        }
    }
}

This code does not work as we can not return a ref to a temporary. How can we fix this? My problem is that for some of the enum Constructors, we have a Vec, in which case we just return a ref. In other situations, we have a finite # of values, in which case we need to construct a Vec to return.

  1. Pre emptive questions:

Can you make B, C, D take Vec? No, I want compile time guarantee of exactly 43/1 items.

Can you return Cow<'_, [f32]> instead?

2 Likes

@vitalyd : Are you suggesting

pub enum Foo {
    A(Vec<f32>),
    B(f32, f32, f32, f32),
    C(f32, f32, f32),
    D(f32, ),
}

impl Foo {
    fn ref_to_f32(&self) -> Cow<Vec<f32>> {
        match self {
            Foo::A(v) => Cow::Borrowed(v),
            Foo::B(a, b, c, d) => Cow::Owned(vec![*a, *b, *c, *d]),
            Foo::C(a, b, c) => Cow::Owned(vec![*a, *b, *c]),
            Foo::D(a) => Cow::Owned(vec![*a]),
        }
    }
}

? I think this will work.

You could easily return &[f32], just need to make your B and C use arrays instead of tuples. D can be handled by slice::from_ref.

2 Likes

@birkenfeld : Although I did not include them in the above example, the enum constrcturos also have non-f32 fields.

Yeah, pretty much that (you can use Cow::from in all the arms rather than using the Owned/Borrowed variants).

I like @birkenfeld’s suggestion if you’re ok with using arrays. You can use an array for the f32s, and different types for the other fields - you can still return a slice from them in that case.

BTW, &Vec<f32> is a useless type. You should almost always use &[f32] instead. The difference between [] and Vec is that the Vec can grow, but &Vec is a read-only vec that can't grow.

There should be another difference: &[f32] is stack-allocated and can cause a stack overflow if it's too big, whereas &Vec<f32> is heap-allocated.
Unfortunately building a Vec is done on the stack in debug builds, so both can segfault your executable if they're too big.

Edit: typed before thinking ...

No, &[f32] is just a (fat) pointer. It can point anywhere, stack, heap or otherwise.

2 Likes

Regardless that &[f32] is stack allocated pointer, I guess you meant, that [f32] is stack allocated which also is not true - its !Sized, so it is impossible to allocate it directly on stack at all. [f32; N] is "stack allocated" (which is not exactly true - where is it allocated is context dependent, but as a local variable it is allocated fully on stack, without any heap allocations).