Skip to content

Unactionable "field is never read" warning for printed structs with the Debug derive #88900

Closed
@cart

Description

@cart

The latest nightly (rustc 1.57.0-nightly (8c2b6ea 2021-09-11)) introduces a new warning that I consider to be invalid.

Given the following code: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=14d790d2f286f58543c42d915f60e182

#[derive(Debug)]
struct Foo {
    a: String,
}

fn main() {
  let foo = Foo {
      a: "hello".to_string(),
  };
  
  println!("{:?}", foo);
}

The current output is:

warning: field is never read: `a`
 --> src/main.rs:3:5
  |
3 |     a: String,
  |     ^^^^^^^^^
  |
  = note: `#[warn(dead_code)]` on by default

warning: `playground` (bin "playground") generated 1 warning
    Finished dev [unoptimized + debuginfo] target(s) in 1.56s

This feels very wrong to me. It means any struct created with the intent to be printed is "invalid" according to the compiler. This warning is not actionable. My only option is to suppress it, live with it, or insert some sort of "dummy read". This has broken our CI builds for Bevy (because we treat warnings as errors).

Activity

added
A-diagnosticsArea: Messages for errors, warnings, and lints
T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
on Sep 12, 2021
hellow554

hellow554 commented on Sep 13, 2021

@hellow554
Contributor

First off: Denying warnings is considered harmful [0] [1] [2] [3] and should not be default in a project. You may use it for testing purposes, but not actively as a blocker.

Second: I can see, that you might think that the lint is wrong, but it is actually right. You never read the field a.
It is used in the Debug output, yes, but not actively in your code.
For the warning to silence you can prepend a underscore (e.g. _a) but that's it.

The same goes for Clone btw.

Here's the relevant PR + a lot of discussion: #85200

cart

cart commented on Sep 13, 2021

@cart
Author
  1. We don't hard-code deny(warnings), we pass in -D warnings as a rustc flag as part of our CI, which doesn't suffer from the "forward compatibility" issues called out in the four links you shared. I think most people would agree that warnings should be either resolved or suppressed, especially in public libraries.
  2. This doesn't change the fact that something uses it. If I were to implement a new Debug trait in a new crate (with an equivalent proc macro derive), then use that crate and derive that version of Debug on my Foo type, I wouldn't get the warning. This isn't about whether or not I directly touch the field. Its about whether something uses the field. As discussed in Ignore derived Clone and Debug implementations during dead code analysis #85200, the fact that Debug doesn't count is a new exception to a rule that applies to literally everything else.

It sounds like ignoring Debug impls was a pragmatic choice to make this lint more useful. Debug is a common / recommended derive for structs, which meant that most structs weren't getting "unused field" lints. Working around this seems reasonable.

However I think it is worth calling out that this specific case is relatively common / many people would consider it "valid". At the very least, I imagine some users would be confused, especially given that this isn't consistent with other traits and printing debug derives is super common. Warnings should be actionable. It is the compiler telling the user that something should be fixed / isn't recommended. In my ideal world either:

  1. The code above is considered valid by rustc: when a Debug impl is actually used via something like println, unused field lints are suppressed (because the fields are actually used by code the user chose to write).
  2. The code above is considered invalid by rustc, but equivalent tools are recommended / provided: if using Debug in this context is incorrect (ex: Display should be used instead), ideally there is a Debug-like derive for Display, and ideally rustc recommends that in these cases.

Of course both of those solutions are complicated / controversial and I'm sure this was supposed to be an easy win. I'm not demanding any action here / this isn't a high priority for me. I'm just reporting a confusing / unintuitive new behavior. I doubt I'll be the last person to hit this.

llogiq

llogiq commented on Sep 13, 2021

@llogiq
Contributor

We have another datapoint at synth. In our case, the code in question is not derived; the type_name field is actually read in a trait impl (though to be fair it's in an async fn, so there may be expansion involved, too).

added
T-langRelevant to the language team
and removed
T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
on Sep 13, 2021
Mark-Simulacrum

Mark-Simulacrum commented on Sep 13, 2021

@Mark-Simulacrum
Member

Nominating this for T-lang awareness, and possible discussion.

FabianWolff

FabianWolff commented on Sep 13, 2021

@FabianWolff
Contributor

We have another datapoint at synth. In our case, the code in question is not derived; the type_name field is actually read in a trait impl (though to be fair it's in an async fn, so there may be expansion involved, too).

@llogiq The piece of code that you link to reads the data_type field, not the type_name field, so the latter may very well be unused.

In general, only automatically derived impls should be ignored; if you find an exception to this, it's a bug (whereas ignoring derived implementations is intended).

cynecx

cynecx commented on Sep 14, 2021

@cynecx
Contributor

I’ve been getting these warnings as well. The struct actually contains a Child (tokio::process::Child) handle. So at drop time of the struct the Child handle drops and eventually cleanups the child process. So it’s not technically true that the field is never read, because the drop glue certainly does.

Edit: On second thought, this warning might have been lurking there all along because the struct has a Debug derive.

FabianWolff

FabianWolff commented on Sep 14, 2021

@FabianWolff
Contributor

Edit: On second thought, this warning might have been lurking there all along because the struct has a Debug derive.

That's what I was going to say. Nothing was changed with regards to the Drop trait, so the behavior you're complaining about must have been there before (it was just hidden by the Debug derive).

tmandry

tmandry commented on Sep 14, 2021

@tmandry
Member

For those wondering about disruptiveness of the change, we had to add this to 93 153 fields in the main Fuchsia repo. I'd say this is among the most disruptive warnings changes we've had to fix. Most of these structs seem to only derive Debug; I haven't dug into what they're used for or whether these fields should exist yet.

https://fuchsia-review.googlesource.com/c/fuchsia/+/581062
https://fuchsia-review.googlesource.com/c/fuchsia/+/581066

64 remaining items

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-diagnosticsArea: Messages for errors, warnings, and lintsA-lintsArea: Lints (warnings about flaws in source code) such as unused_mut.L-dead_codeLint: dead_codeT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.T-langRelevant to the language team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @Turbo87@peterjoel@nikomatsakis@jpalus@hellow554

        Issue actions

          Unactionable "field is never read" warning for printed structs with the Debug derive · Issue #88900 · rust-lang/rust