`#[externally_constructed]` or anything else?

After Detect pub structs never constructed and unused associated constants by mu001999 · Pull Request #125572 · rust-lang/rust · GitHub, rustc lints pub struct with private fields if they don't provide pub constructors.

But in FFI crates like libc and windows-rs, some structs with #[repr(C)] are commonly used, and they don't provide pub constructors, see more in dead_code lint wrongly warns about public repr(C) structs with private fields but no constructors · Issue #126169 · rust-lang/rust · GitHub.

@RalfJung said "maybe we need an #[externally_constructed] attribute or so".

So I opened Add a new attr indicates pub struct with private fields could be contructed externally by mu001999 · Pull Request #126456 · rust-lang/rust · GitHub and Tracking Issue for `#![feature(externally_constructed_attr)]` · Issue #126634 · rust-lang/rust · GitHub, which try to add the attr externally_constructed which indicates the struct will be constructed externally although it doesn't have a pub constructor and then avoid linting it in dead code analysis.

Do you think it's a good idea or there is another better way?

I'd rather see a dedicated lint name to be #[allow]ed, unless #[externally_constructed] has behavior beyond suppressing the lint. Don't introduce new(-looking) mechanisms when old ones will do.

6 Likes

Alternately: why are your repr(C) structs pub with private fields? The C side of the bridge won’t have private fields.

(I think there are valid answers to this, like “this would require exposing a bunch of other types that the rest of my Rust code doesn’t care about”, but it still seems worth considering.)

1 Like

The fields could have safety invariants, which in C are expressed in documentation and in Rust (currently) must be expressed via making the fields private, since fields can't (yet) be declared unsafe-to-write.

1 Like

#[allow(dead_code)] can work, do you think we need another lint name for this?

A common case is representing incomplete/opaque types used in FFI. Ideally these would be extern type but until those are stable the pattern is to use private fields to block incorrect creation of the type.

(Very oddly, I'm getting the warning on a single type in my project, despite having multiple such types. I've yet to investigate what's different about it, but I would guess it's to due with the types' usage in extern fn.)

But I agree that the "correct" way to suppress the lint is just #[allow] rather than a separate new attribute.