Skip to content

Confusing error message when a trait bound is unsatisfied due to lack of mutability #63619

Closed
@zackw

Description

@zackw

In this code, fp should have been declared as a mutable reference, and the compiler gives you a nice clear error message saying so:

use std::env::args;
use std::fs::File;
use std::io::{stdout, Write};

fn main() {
    let mut args = args();
    let _ = args.next();
    let dest = args.next();

    let h1; let h2; let h3;

    let fp: &dyn Write = match dest {
        Some(path) => { h1 = File::create(path).unwrap(); &h1 },
        None => { h2 = stdout(); h3 = h2.lock(); &h3 }
    };

    writeln!(fp, "hello world").unwrap();
}

error[E0596]: cannot borrow `*fp` as mutable, as it is behind a `&` reference
  --> test.rs:17:14
   |
12 |     let fp: &dyn Write = match dest {
   |         -- help: consider changing this to be a mutable reference:
   |            `&mut dyn std::io::Write`
...
17 |     writeln!(fp, "hello world").unwrap();
   |              ^^ `fp` is a `&` reference, so the data it refers to
   |                 cannot be borrowed as mutable

But you have a BufWriter in between writeln! and the match, you get a different and very confusing error message:

use std::env::args;
use std::fs::File;
use std::io::{stdout, Write, BufWriter};

fn main() {
    let mut args = args();
    let _ = args.next();
    let dest = args.next();

    let h1; let h2; let h3;

    let fp: &dyn Write = match dest {
        Some(path) => { h1 = File::create(path).unwrap(); &h1 },
        None => { h2 = stdout(); h3 = h2.lock(); &h3 }
    };

    let fp = BufWriter::new(fp);

    writeln!(fp, "hello world").unwrap();
}

error[E0277]: the trait bound `&dyn std::io::Write: std::io::Write`
              is not satisfied
  --> test.rs:17:14
   |
17 |     let fp = BufWriter::new(fp);
   |              ^^^^^^^^^^^^^^ the trait `std::io::Write`
   |           is not implemented for `&dyn std::io::Write`
   |
   = note: required by `std::io::BufWriter::<W>::new`

How can it possibly be that "the trait std::io::Write is not implemented for &dyn std::io::Write" ?! Well, in fact, it's the exact same problem as the first sample code, and the error message is literally true. &dyn std::io::Write doesn't implement std::io::Write, but &mut dyn std::io::Write does.

Could the compiler perhaps print an additional note when &T doesn't satisfy a trait bound, but &mut T would have, explaining that this might be the problem? Or vice versa, of course.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-diagnosticsArea: Messages for errors, warnings, and lintsA-trait-systemArea: Trait systemC-enhancementCategory: An issue proposing an enhancement or a PR with one.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions