Less typing for Push in an Option<Vec<MyType>>?

Hi,

I need the cleanest way to simplify the writing of pushing value to a vector.
Still in my learning process of Rust, so I may have missed the elephant in the room.

Below are two equivalent variants. I plan to go for the macro one but is there a better standard way to do it ( no need to reinvent everything)?

#[derive(Debug, Default)]
struct A {
    vec: Option<Vec<u32>>,
}
macro_rules! pusher {
    ($opt_vec : expr, $value : expr ) => {
        if let None = $opt_vec {
            $opt_vec = Some(vec![]);
        }

        if let Some(ref mut v) = $opt_vec {
            v.push($value);
        }
    };
}

fn test1() {
    let mut a: A = Default::default();

    if let None = a.vec {
        a.vec = Some(vec![]);
    }

    if let Some(ref mut v) = a.vec { // <- looks like we should know it's ok, no ?
        v.push(1);
    }

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

fn test2() {
    let mut a: A = Default::default();

    pusher!(a.vec, 1);
    println!("{:?}", a);
}

fn main() {
    test1();
    test2();
}

(Playground)

Output:

A { vec: Some([1]) }
A { vec: Some([1]) }

Thanks!

you can do this with

// the following are equivalent since `Vec` implements `Default`
a.vec.get_or_insert(vec![]).push(1);
a.vec.get_or_insert_with(Vec::new).push(1);
a.vec.get_or_insert_default().push(1);
5 Likes

That's huge
I will use that

Thanks a lot :+1:

Already solved, but I wanted to mention:

If you will never have the state: Some([]) empty vector, then you can also use just Vec. An empty Vec does not allocate.
Any place where you want to "option-ify" the vec for downstream use:

(!a.vec.is_empty()).then_some(a.vec)
1 Like

As an additional hint, I do not recommend write macros when you can avoid it because the error messages when developing and (mis-)using are worse.

You can write your macro as a generic free function or you can use the extension trait pattern.

fn pusher<T>(optvec: &mut Option<Vec<T>>, val: T) {
    optvec.get_or_insert_with(Vec::new).push(val);
}

trait OptVecExt<T> {
    fn push(&mut self, val: T);
    fn pop(&mut self) -> Option<T>;
}

impl<T> OptVecExt<T> for Option<Vec<T>> {
    fn push(&mut self, val: T) {
        self.get_or_insert_with(Vec::new).push(val);
    }
    fn pop(&mut self) -> Option<T> {
        self.and_then(|vec| vec.pop())
    }
}

fn some_other_function(v: Option<Vec<i32>>) {
    v.push(42);
}
1 Like

Thanks a lot guys,
Very useful info here! Didn't have a notification of your inputs, sorry for the delay.

I've been coding in rust intensively for a few days now and I really like it.

:+1:

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.