A simple example of HRBT
(Jin Qing’s Column, April., 2025)
Higher-ranked trait bounds (HRTBs) specify a bound that is true for all lifetimes.
Here is an example of how HRTB is used.
fn bar() -> impl for<'a> Fn(&'a str, &'a str) -> &'a str {
move |text1, text2| {
println!("{text1}, {text2}");
text1
}
}
bar() returns a Fn which must specify the lifetimes of parameters and return type.
The main() function:
fn main() {
let func = bar();
let text1: String = "Hello".into();
let text2: String = "world".into();
let text = func(&text1, &text2);
println!("text: {text}");
}
If we don’t use HRBT:
fn bar<'a>() -> impl Fn(&'a str, &'a str) -> &'a str {
todo!()
}
There will be errors:
error[E0597]: `text1` does not live long enough
--> src\main.rs:5:21
|
3 | let text1: String = "Hello".into();
| ----- binding `text1` declared here
4 | let text2: String = "world".into();
5 | let text = func(&text1, &text2);
| ^^^^^^ borrowed value does not live long enough
6 | println!("text: {text}");
7 | }
| -
| |
| `text1` dropped here while still borrowed
| borrow might be used here, when `func` is dropped and runs the destructor for type `impl Fn(&str, &str) -> &str`
|
= note: values in a scope are dropped in the opposite order they are defined
error[E0597]: `text2` does not live long enough
--> src\main.rs:5:29
|
4 | let text2: String = "world".into();
| ----- binding `text2` declared here
5 | let text = func(&text1, &text2);
| ^^^^^^ borrowed value does not live long enough
6 | println!("text: {text}");
7 | }
| -
| |
| `text2` dropped here while still borrowed
| borrow might be used here, when `func` is dropped and runs the destructor for type `impl Fn(&str, &str) -> &str`
|
= note: values in a scope are dropped in the opposite order they are defined
If we delete the lifetimes:
fn bar() -> impl Fn(&str, &str) -> &str {
todo!()
}
The compiler sugguests using HRBT:
error[E0106]: missing lifetime specifier
--> src\main.rs:9:36
|
9 | fn bar() -> impl Fn(&str, &str) -> &str {
| ---- ---- ^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value, but the signature does
not say whether it is borrowed from argument 1 or argument 2
= note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html
help: consider making the bound lifetime-generic with a new `'a` lifetime
|
9 | fn bar() -> impl for<'a> Fn(&'a str, &'a str) -> &'a str {
| +++++++ ++ ++ ++
help: consider introducing a named lifetime parameter
|
9 | fn bar<'a>() -> impl Fn(&'a str, &'a str) -> &'a str {
| ++++ ++ ++ ++
Reference: https://www.zhihu.com/question/504670139