rust-clippy
rust-clippy copied to clipboard
`clippy::vec_init_then_push` can't be ignored
Summary
I'm trying to come up with a configuration array that takes objects that are of a given type. The items in this global list can vary depending on compilation settings. As a result, I'm constructing the values in a LazyLock.
The items that are added to the vec depend on feature flags. As a result, I can't construct a vec![] as it suggests. Additionally, I can't seem to be able to turn them off in the obvious way.
Lint Name
vec_init_then_push
Reproducer
I tried this code:
#![allow(unexpected_cfgs)]
use std::sync::RwLock;
use std::sync::LazyLock;
trait ExampleTrait: Send + Sync {
fn value(&self) -> u32;
}
struct Object {
val: u32,
}
impl ExampleTrait for Object {
fn value(&self) -> u32 {
self.val
}
}
fn init_object(val: u32) -> Object {
Object { val }
}
static OBJECTS: LazyLock<RwLock<Vec<Box<dyn ExampleTrait>>>> = LazyLock::new(|| {
#[allow(clippy::vec_init_then_push)]
let mut v: Vec<Box<dyn ExampleTrait>> = vec![];
#[allow(clippy::vec_init_then_push)]
v.push(Box::new(init_object(1)));
#[cfg(feature = "other-feature")]
#[allow(clippy::vec_init_then_push)]
v.push(Box::new(init_object(2)));
RwLock::new(v)
});
fn main() {
let test_list = &*OBJECTS.read().unwrap();
for val in test_list {
println!("Val: {}", val.value());
}
}
I saw this happen:
warning: calls to `push` immediately after creation
--> src/main.rs:25:5
|
25 | / let mut v: Vec<Box<dyn ExampleTrait>> = vec![];
26 | |
27 | | #[allow(clippy::vec_init_then_push)]
28 | | v.push(Box::new(init_object(1)));
| |_____________________________________^ help: consider using the `vec![]` macro: `let v: Vec<Box<dyn ExampleTrait>> = vec![..];`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#vec_init_then_push
= note: `#[warn(clippy::vec_init_then_push)]` on by default
I expected to see this happen:
No output, especially since I ignored the lint immediately above.
Version
rustc 1.87.0 (17067e9ac 2025-05-09)
binary: rustc
commit-hash: 17067e9ac6d7ecb70e50f92c1944e545188d2359
commit-date: 2025-05-09
host: x86_64-unknown-linux-gnu
release: 1.87.0
LLVM version: 20.1.1
Additional Labels
No response
The way the lint is currently implemented, the #[allow] attribute needs to go on the block (or since that's unstable, putting it on the static itself should work too). Accepting lint level attrs at any of the push() calls could maybe work, but it would probably get messy with #[expect]
It's a bit incongruous to see the allow(..) in the warning, and took me longer than I'd like to figure out how to turn it off.
Is there another way this should be done to avoid the warning? Or is there a way to hint how it expects the warning to be disabled?
#![allow(unexpected_cfgs)]
#![deny(clippy::vec_init_then_push)]
use std::sync::RwLock;
use std::sync::LazyLock;
trait ExampleTrait: Send + Sync {
fn value(&self) -> u32;
}
struct Object {
val: u32,
}
impl ExampleTrait for Object {
fn value(&self) -> u32 {
self.val
}
}
fn init_object(val: u32) -> Object {
Object { val }
}
// TODO(you): rename this function
#[allow(clippy::vec_init_then_push)]
fn x() -> RwLock<Vec<Box<dyn ExampleTrait>>> {
let mut v: Vec<Box<dyn ExampleTrait>> = vec![];
v.push(Box::new(init_object(1)));
#[cfg(feature = "other-feature")]
v.push(Box::new(init_object(2)));
RwLock::new(v)
}
static OBJECTS: LazyLock<RwLock<Vec<Box<dyn ExampleTrait>>>> = LazyLock::new(x);
fn main() {
let test_list = &*OBJECTS.read().unwrap();
for val in test_list {
println!("Val: {}", val.value());
}
}
this should work (playground).