Skip to content

Diagnostics: suggest higher-rank trait bounds when implementing trait for &Type #69511

Closed
@yoshuawuyts

Description

@yoshuawuyts

Failing Code

Consider the following code:

use std::io::{self, prelude::*};
use std::sync::Arc;

struct IoArc<T>(Arc<T>);

impl<T> Read for IoArc<T>
where
    &T: Read,
{
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        (&mut &*self.0).read(buf)
    }
}

playground

This fails with a pretty gnarly error about how &IoArc<T> doesn't have a
lifetime, parameter T doesn't live long enough, and we should consider adding
a lifetime. It (wrongly) suggest we may want to add a static lifetime. Which
is not the right solution.

Compiling playground v0.0.1 (/playground)
error[E0637]: `&` without an explicit lifetime name cannot be used here
 --> src/lib.rs:8:5
  |
8 |     &T: Read,
  |     ^ explicit lifetime name needed here

error[E0310]: the parameter type `T` may not live long enough
  --> src/lib.rs:6:1
   |
6  |   impl<T> Read for IoArc<T>
   |   ^    - help: consider adding an explicit lifetime bound `T: 'static`...
   |  _|
   | |
7  | | where
8  | |     &T: Read,
9  | | {
...  |
12 | |     }
13 | | }
   | |_^
   |
note: ...so that the reference type `&'static T` does not outlive the data it points at
  --> src/lib.rs:6:1
   |
6  | / impl<T> Read for IoArc<T>
7  | | where
8  | |     &T: Read,
9  | | {
...  |
12 | |     }
13 | | }
   | |_^

error[E0310]: the parameter type `T` may not live long enough
  --> src/lib.rs:10:5
   |
6  |   impl<T> Read for IoArc<T>
   |        - help: consider adding an explicit lifetime bound `T: 'static`...
...
10 | /     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
11 | |         (&mut &*self.0).read(buf)
12 | |     }
   | |_____^
   |
note: ...so that the reference type `&'static T` does not outlive the data it points at
  --> src/lib.rs:10:5
   |
10 | /     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
11 | |         (&mut &*self.0).read(buf)
12 | |     }
   | |_____^

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0310`.
error: could not compile `playground`.

To learn more, run the command again with --verbose.

Working Code

Instead the resolution is to add a higher-rank trait bound, like this:

use std::io::{self, prelude::*};
use std::sync::Arc;

struct IoArc<T>(Arc<T>);

impl<T> Read for IoArc<T>
where
    for<'a> &'a T: Read, // <-- we added for<'a> to get a lifetime
{
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        (&mut &*self.0).read(buf)
    }
}

Diagnostics Suggestions

I understand that this is not a particularly common pattern, but the compiler
really isn't of much help when trying to figure this out. A better suggestion
here would have been:

Compiling playground v0.0.1 (/playground)
error[E0637]: `&` without an explicit lifetime name cannot be used here
 --> src/lib.rs:8:5
  |
8 |     &T: Read,
  |     ^ explicit lifetime name needed here

help: Consider adding a higher-rank trait bound:

 --> src/lib.rs:8:5
  |
8 |     for<'a> &'a T: Read,
  |

With if 'static is sometimes the right solution, perhaps two help texts would make sense:

help: ...or use a static lifetime:

 --> src/lib.rs:8:5
  |
8 |     &'static T: Read,
  |

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-diagnosticsArea: Messages for errors, warnings, and lintsC-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