Micro-feature: if in vec![]

Here’s a candidate macro idea for example.

Definition:

#[doc(hidden)]
pub mod internal {
    pub use std::iter::{Iterator, IntoIterator, once, Extend};
    pub use Vec;

    #[macro_export]
    macro_rules! vecc_internal {
        ([$($x:ident)*] ...$e:expr, $($t:tt)*) => {
            match $crate::internal::IntoIterator::into_iter($e) {
                x => $crate::vecc_internal!([$($x)* x] $($t)*)
            }
        };
        ([$($x:ident)*] $e:expr, $($t:tt)*) => {
            $crate::vecc_internal!([$($x)*] ...$crate::internal::once($e), $($t)*)
        };
        ([$($x:ident)*] ...$e:expr) => {
            $crate::vecc_internal!([$($x)*] ...$e,)
        };
        ([$($x:ident)*] $e:expr) => {
            $crate::vecc_internal!([$($x)*] $e,)
        };
        ([$($x:ident)*]) => {
            {
                let mut v = $crate::internal::Vec::with_capacity(
                    0_usize $(+ $crate::internal::Iterator::size_hint(&$x).0)*
                );
                $($crate::internal::Extend::extend(&mut v, $x);)*
                v
            }
        };
    }
}

#[macro_export]
macro_rules! vecc {
    ($($t:tt)*) => {
        $crate::vecc_internal!([] $($t)*)
    }
}

Use case:

fn main() {
    let v1: Vec<usize> = vecc![0];
    let v2 = vecc![0];
    let v3 = vecc![1, ...2..=3, 4]; // or equivalently vecc![1, ...(2..=3), 4]
    let condition = true;
    let v4 = vecc![1, ...condition.then(|| 2), 4];
    let condition = false;
    let v5 = vecc![1, ...condition.then(|| 2), 4];
    let condition = true;
    let v6 = vecc![1, ...condition.then(|| [2, 3]).into_iter().flatten(), 4];
    let condition = false;
    let v7 = vecc![1, ...condition.then(|| [2, 3]).into_iter().flatten(), 4];
    
    dbg!(v1, v2, v3, v4, v5, v6, v7);
}
[src/main.rs:54] v1 = [
    0,
]
[src/main.rs:54] v2 = [
    0,
]
[src/main.rs:54] v3 = [
    1,
    2,
    3,
    4,
]
[src/main.rs:54] v4 = [
    1,
    2,
    4,
]
[src/main.rs:54] v5 = [
    1,
    4,
]
[src/main.rs:54] v6 = [
    1,
    2,
    3,
    4,
]
[src/main.rs:54] v7 = [
    1,
    4,
]

Rust Playground

By the way, the size hints in the above examples are all accurate, so there’s no reallocation: Rust Playground

[src/main.rs:43] 0_usize + crate::internal::Iterator::size_hint(&x).0 = 1
[src/main.rs:43] v.len() = 1
[src/main.rs:44] 0_usize + crate::internal::Iterator::size_hint(&x).0 = 1
[src/main.rs:44] v.len() = 1
[src/main.rs:45] 0_usize + crate::internal::Iterator::size_hint(&x).0 +
        crate::internal::Iterator::size_hint(&x).0 +
    crate::internal::Iterator::size_hint(&x).0 = 4
[src/main.rs:45] v.len() = 4
[src/main.rs:47] 0_usize + crate::internal::Iterator::size_hint(&x).0 +
        crate::internal::Iterator::size_hint(&x).0 +
    crate::internal::Iterator::size_hint(&x).0 = 3
[src/main.rs:47] v.len() = 3
[src/main.rs:49] 0_usize + crate::internal::Iterator::size_hint(&x).0 +
        crate::internal::Iterator::size_hint(&x).0 +
    crate::internal::Iterator::size_hint(&x).0 = 2
[src/main.rs:49] v.len() = 2
[src/main.rs:51] 0_usize + crate::internal::Iterator::size_hint(&x).0 +
        crate::internal::Iterator::size_hint(&x).0 +
    crate::internal::Iterator::size_hint(&x).0 = 4
[src/main.rs:51] v.len() = 4
[src/main.rs:53] 0_usize + crate::internal::Iterator::size_hint(&x).0 +
        crate::internal::Iterator::size_hint(&x).0 +
    crate::internal::Iterator::size_hint(&x).0 = 2
[src/main.rs:53] v.len() = 2

(yes, the fact that they’re all xs looks a bit weird, but the spans are different; hygiene and stuff…)

Naturally, I haven’t benchmarked the performance of this particular implementation at all yet; in particular the case of no ...s being used at all should probably fall back to the existing implementation :slight_smile:

5 Likes