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 definederror[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