A function can borrow data even when its return type shows no reference. Consider this valid function, which triggers a compiler warning:
pub fn items(scores: &[u8]) -> std::slice::Iter<u8> {
scores.iter()
}
Iter has both a lifetime parameter and an element type. Writing Iter<u8>
hides the lifetime parameter. The mismatched_lifetime_syntaxes warning asks
you to make its relationship to the input easier to see. With warnings denied,
as in this site's Rust checks, the warning becomes an error.
Add '_ to the return type:
pub fn items(scores: &[u8]) -> std::slice::Iter<'_, u8> {
scores.iter()
}
This changes the notation, not what the function borrows or how long the data lives. The iterator still borrows the input slice.
Three forms of lifetime syntax
| Form | Examples | What is visible? |
|---|---|---|
| Named | &'a str, View<'a> |
An explicit lifetime name |
| Elided | &str, &'_ str, View<'_> |
A reference or lifetime placeholder |
| Hidden | View, when declared as View<'a> |
No visible lifetime argument in the path |
The lint checks inconsistent forms for the same lifetime across a function's
input and output. It does not require every lifetime in a signature to have
the same name or syntax. In particular, &str and &'_ str belong to the same
group. See the compiler's lint guide.
'_ asks Rust to infer the lifetime. In this example, the single input
lifetime determines the output lifetime under the
elision rules.
A placeholder does not remove borrowing restrictions or resolve every ambiguous
signature. A function borrowing from either of two independent inputs may
still need named lifetimes to state the relationship.
Check the specific warning
These are complete function definitions. Which ones trigger
mismatched_lifetime_syntaxes?
pub struct View<'a> {
pub text: &'a str,
}
pub fn first<'a>(view: &'a View<'a>) -> &str {
view.text
}
pub fn second(view: View) -> View {
view
}
pub fn third(text: &str) -> std::str::Chars<'_> {
text.chars()
}
pub fn fourth<'a>(text: &'a str) -> std::str::Chars<'a> {
text.chars()
}
first triggers the warning: its named input lifetime is elided in the return
type. second uses hidden lifetimes on both sides. third uses elided forms
on both sides, and fourth uses a name consistently. Those three do not trigger
this particular warning.
These answers concern this particular lint. A clean result for it does not
establish that a larger program passes every compiler and Clippy check.
For second, writing View<'_> on both sides makes the borrowing visible
even though this compiler lint does not require the change.
See what the return value borrows
Here is a complete example with a borrowed configuration view:
pub struct Config<'a> {
pub root: &'a str,
}
pub fn load(text: &str) -> Config<'_> {
Config { root: text }
}
fn main() {
let text = String::from("assets");
let config = load(&text);
assert_eq!(config.root, "assets");
}
In this implementation, config.root refers into text. You cannot destroy
text and then use that reference. The borrow checker enforces this even if
you hide the return type's lifetime.
Choose a borrowed view when sharing the caller's text fits the API. Store a
String instead when the result should own its text independently. The lint
does not decide which design is appropriate. Nor does a lifetime parameter by
itself prove that every implementation returns data from that input: a
reference to static data could also satisfy this signature.
Apply the fix
Read the diagnostic's input and output spans. For a simple inferred borrow,
add '_ in the return type's lifetime slot, as in Iter<'_, u8>. If a name is
needed to express a relationship, use that name consistently instead.
Check the type's declaration for argument order. std::str::Split<'_, char>
has a lifetime argument followed by its pattern type. Writing Split<'_>
omits that required type argument.
The separate elided_lifetimes_in_paths lint warns about hidden path lifetimes
even when used consistently. It is allow-by-default on the checked toolchain;
projects can enable it explicitly. Use mismatched_lifetime_syntaxes when
configuring the consistency lint rather than copying an older lint name.
You can also return impl Iterator<Item = &u8> + '_ to hide the concrete
iterator type. That changes the public API: callers only get the capabilities
promised by the return type, rather than all of slice::Iter's methods and
iterator traits. Choose it for that API design, not merely to silence a warning.