Summary
unnecessary_cast suppresses casts of values whose type might be a cfg-dependent type alias (e.g. libc struct fields) by conservatively skipping when the cast source involves an external function it can't inspect. But that suppression only matches free functions (Res::Def(DefKind::Fn, _) on a path expr), so two semantically equivalent initializations of the same local lint differently: initializing via std::mem::zeroed() suppresses the lint, while initializing via MaybeUninit::uninit() + .assume_init() does not — assume_init is a method call (no callee path expr) and MaybeUninit::uninit resolves to DefKind::AssocFn, which the check doesn't match.
The cast in the reproducer is genuinely platform-dependent (statvfs.f_blocks is fsblkcnt_t, not u64 on every target), so the lint's own rationale for the alias suppression applies equally to both forms. The practical effect is that migrating FFI out-params from mem::zeroed() to the more idiomatic MaybeUninit surfaces this false positive and forces an #[allow].
Lint Name
unnecessary_cast
Reproducer
I tried this code (with libc = "0.2", on x86_64-unknown-linux-gnu):
fn with_zeroed(path: &std::ffi::CStr) -> u64 {
let mut s: libc::statvfs = unsafe { std::mem::zeroed() };
unsafe { libc::statvfs(path.as_ptr(), &mut s) };
s.f_blocks as u64
}
fn with_maybe_uninit(path: &std::ffi::CStr) -> u64 {
let mut s = std::mem::MaybeUninit::<libc::statvfs>::uninit();
unsafe { libc::statvfs(path.as_ptr(), s.as_mut_ptr()) };
let s = unsafe { s.assume_init() };
s.f_blocks as u64
}
I expected to see this happen: both functions treated the same — no lint on either, since f_blocks is the cfg-dependent alias fsblkcnt_t and the cast is required on targets where it isn't u64.
Instead, this happened: only the MaybeUninit version lints:
warning: casting to the same type is unnecessary (`u64` -> `u64`)
--> src/main.rs:11:5
|
11 | s.f_blocks as u64
| ^^^^^^^^^^^^^^^^^ help: try: `s.f_blocks`
The cause is in is_cast_from_ty_alias: walking the local's initializer, std::mem::zeroed is a path resolving to DefKind::Fn and hits the "External function, we can't know, better be safe" break, while MaybeUninit::uninit resolves to DefKind::AssocFn (not matched) and assume_init is an ExprKind::MethodCall (never seen as a path), so no suppression applies. Field accesses on external structs aren't examined at all, so the actual alias (fsblkcnt_t) is invisible either way.
Related: #8018, #8093 (alias/cfg-dependent FPs in this lint), #6466 (same libc-field pattern in useless_conversion).
Version
rustc 1.96.0 (ac68faa20 2026-05-25)
clippy 0.1.96 (ac68faa20c 2026-05-25)
Additional Labels
@rustbot label +I-false-positive
Summary
unnecessary_castsuppresses casts of values whose type might be a cfg-dependent type alias (e.g. libc struct fields) by conservatively skipping when the cast source involves an external function it can't inspect. But that suppression only matches free functions (Res::Def(DefKind::Fn, _)on a path expr), so two semantically equivalent initializations of the same local lint differently: initializing viastd::mem::zeroed()suppresses the lint, while initializing viaMaybeUninit::uninit()+.assume_init()does not —assume_initis a method call (no callee path expr) andMaybeUninit::uninitresolves toDefKind::AssocFn, which the check doesn't match.The cast in the reproducer is genuinely platform-dependent (
statvfs.f_blocksisfsblkcnt_t, notu64on every target), so the lint's own rationale for the alias suppression applies equally to both forms. The practical effect is that migrating FFI out-params frommem::zeroed()to the more idiomaticMaybeUninitsurfaces this false positive and forces an#[allow].Lint Name
unnecessary_cast
Reproducer
I tried this code (with
libc = "0.2", on x86_64-unknown-linux-gnu):I expected to see this happen: both functions treated the same — no lint on either, since
f_blocksis the cfg-dependent aliasfsblkcnt_tand the cast is required on targets where it isn'tu64.Instead, this happened: only the
MaybeUninitversion lints:The cause is in
is_cast_from_ty_alias: walking the local's initializer,std::mem::zeroedis a path resolving toDefKind::Fnand hits the "External function, we can't know, better be safe" break, whileMaybeUninit::uninitresolves toDefKind::AssocFn(not matched) andassume_initis anExprKind::MethodCall(never seen as a path), so no suppression applies. Field accesses on external structs aren't examined at all, so the actual alias (fsblkcnt_t) is invisible either way.Related: #8018, #8093 (alias/cfg-dependent FPs in this lint), #6466 (same libc-field pattern in
useless_conversion).Version
Additional Labels
@rustbot label +I-false-positive