Higher-Ranked Trait Bounds
In trait definitions and implementations, Higher-Ranked Trait Bounds (HRTBs, for<'a>) allow a trait itself—or a blanket implementation—to require that a type or supertrait holds across every possible lifetime 'a simultaneously.
Higher-Ranked Supertraits (trait OwnedDeserializer: for<'de> Deserialize<'de>)
Zero-copy deserialization frameworks (such as serde) distinguish between two kinds of deserializable types:
Deserialize<'de>: Types that can borrow zero-copy slices (&'de str,&'de [u8]) from an input buffer living for'de.DeserializeOwned: Types (likeString,Vec<u8>,u64) that own all of their data and can therefore be deserialized from a temporary buffer of any lifetime'de—even a buffer that is dropped immediately before the parser function returns!
Using a higher-ranked supertrait bound pub trait DeserializeOwned: for<'de> Deserialize<'de> {} paired with a blanket implementation expresses this relationship in three lines:
pub trait FromWire<'wire>: Sized {
fn parse(raw: &'wire str) -> Option<Self>;
}
// Higher-ranked supertrait: any `FromWireOwned` type implements `FromWire<'wire>`
// for EVERY possible lifetime `'wire`:
pub trait FromWireOwned: for<'wire> FromWire<'wire> {}
impl<T> FromWireOwned for T where for<'wire> T: FromWire<'wire> {}
// 1. Zero-copy borrowed type: implements `FromWire<'wire>` ONLY for that `'wire`
// (does NOT implement `FromWireOwned`!):
#[derive(Debug)]
struct BorrowedToken<'wire>(&'wire str);
impl<'wire> FromWire<'wire> for BorrowedToken<'wire> {
fn parse(raw: &'wire str) -> Option<Self> {
Some(BorrowedToken(raw.trim()))
}
}
// 2. Owned type: implements `FromWire<'wire>` for ANY `'wire`
// (automatically implements `FromWireOwned`!):
#[derive(Debug)]
struct OwnedToken(String);
impl<'wire> FromWire<'wire> for OwnedToken {
fn parse(raw: &'wire str) -> Option<Self> {
Some(OwnedToken(raw.trim().to_string()))
}
}
// Reads into a temporary local buffer, parses `T`, and drops the buffer!
// Requires `T: FromWireOwned` because the temporary buffer does not outlive the call:
fn read_and_parse_owned<T: FromWireOwned>() -> Option<T> {
let ephemeral_buffer = String::from(" owned-payload-123 ");
T::parse(&ephemeral_buffer)
}
fn main() {
let owned: OwnedToken = read_and_parse_owned().unwrap();
println!("parsed from ephemeral buffer: {:?}", owned.0);
let persistent = String::from(" zero-copy-view ");
let borrowed = BorrowedToken::parse(&persistent).unwrap();
println!("parsed zero-copy view: {:?}", borrowed.0);
}
parsed from ephemeral buffer: "owned-payload-123"
parsed zero-copy view: "zero-copy-view"
Implementing a Trait for Higher-Ranked Function Pointers vs. Specific-Lifetime Pointers
In Rust's type system, for<'a> fn(&'a str) (a single function pointer polymorphic over all lifetimes) is a subtype of fn(&'static str). When implementing a trait for function pointers, impl<F: for<'a> Fn(&'a str)> Handler for F covers all lifetime-generic closures and functions.