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,
]
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 x
s 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