Description
When the lifetime of a value is coerced to a longer one due to the use of an associated function or trait member and the previous lifetime used by other call sites is no longer sufficient, rustc no longer includes any mention of the coercion site (and includes a red herring that forces one to go chasing after the wrong goose). This makes it much, much harder to figure out why code is not working as all the locations rustc mentions are perfectly fine and have nothing to do with the actual cause of the error.
Code
use std::path::{Path, PathBuf};
enum PolyStr<'a> {
Str(&'a str),
}
impl<'a> From<&'static str> for PolyStr<'static> {
fn from(s: &'static str) -> Self {
PolyStr::Str(s)
}
}
#[derive(Default)]
struct Ffcommand<'a> {
inputs: Vec<&'a Path>,
output: Option<PolyStr<'a>>,
}
impl<'a> Ffcommand<'a> {
pub fn run(&self) -> bool {
true
}
pub fn input(mut self, p: &'a Path) -> Self {
self.inputs.push(p);
self
}
pub fn output<P>(mut self, p: P) -> Self
where
P: Into<PolyStr<'a>> + 'a
{
self.output = Some(p.into());
self
}
}
#[derive(Default)]
struct Ffinfo<'a> {
cmd: Ffcommand<'a>,
path: PathBuf,
}
impl Ffinfo<'_> {
fn foo(&self) -> bool {
let cmd = Ffcommand::default()
.input(&self.path)
// uncomment the next line to see the error
// .output("pipe:1")
;
cmd.run()
}
}
fn main() {
let mut info = Ffinfo::default();
info.path = PathBuf::from("./hello.world");
info.foo();
}
When the commented-out line is added back in, rustc will emit the following error:
error[[E0521]](https://doc.rust-lang.org/beta/error-index.html#E0521): borrowed data escapes outside of associated function
--> src/main.rs:46:19
|
45 | fn foo(&self) -> bool {
| -----
| |
| `self` is a reference that is only valid in the associated function body
| let's call the lifetime of this reference `'1`
46 | let cmd = Ffcommand::default()
| ___________________^
47 | | .input(&self.path)
| | ^
| | |
| |______________________________`self` escapes the associated function body here
| argument requires that `'1` must outlive `'static`
For more information about this error, try `rustc --explain E0521`.
error: could not compile `playground` due to previous error
The error message points to self
and .input()
as the locations pertaining to this error, when in fact the error stems entirely from the usage of .output()
, which coerces the lifetime to 'static
due to the trait impl, at the same time as .input()
, which doesn't last as long.
rustc stable (1.62) emits the following error message instead, which is infinitely more helpful:
error[[E0759]](https://doc.rust-lang.org/stable/error-index.html#E0759): `self` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
--> src/main.rs:47:20
|
45 | fn foo(&self) -> bool {
| ----- this data with an anonymous lifetime `'_`...
46 | let cmd = Ffcommand::default()
47 | .input(&self.path)
| ^^^^^^^^^^ ...is used here...
48 | .output("pipe:1")
| ------ ...and is required to live as long as `'static` here
For more information about this error, try `rustc --explain E0759`.
error: could not compile `playground` due to previous error
Notice that here the correct call sites (.input()
and .output()
) are mentioned. In particular, the fact that .output()
is where 'static
is required is clearly mentioned, making it easy to figure out what is going on.
Needless to say, this is a highly stripped-down example for illustration purposes. It took me perhaps half-an-hour to chase down the actual issue in a real codebase; IIRC the .output()
equivalent was part of the original code and .input()
was added some months later, and there were a lot of false trails to chase down before finding the .output()
lifetime coercion.
Version it worked on
It most recently worked on: 1.62.0
Version with regression
rustc --version --verbose
:
1.63.0-beta.5 2022-07-10 93ef0cd7fd86d3d05cee