Taking @Enet4's example,
struct MyData<Collection> {
collection: Collection,
}
impl<Collection> MyData<Collection>
where
for</*all*/ 'iter> &'iter Collection: IntoIterator<Item = &'iter u8>,
{
fn do_something (self: &'_ Self)
{
self.collection
.into_iter()
.filter(|&&x| x == 5)
.for_each(|x| {
println!("{:?}", x);
});
}
}
It is best if the bound is written on the function requiring it (allows adding to the same impl
block other methods / functions that do not require such bounds):
impl<Collection> MyData<Collection>
{
fn do_something (self: &'_ Self)
where
for</*all*/ 'iter> &'iter Collection: IntoIterator<Item = &'iter u8>,
{
self.collection
.into_iter()
.filter(|&&x| x == 5)
.for_each(|x| {
println!("{:?}", x);
});
}
}
which, with lifetime elision, becomes:
impl<Collection> MyData<Collection>
{
fn do_something<'call_site> (self: &'call_site Self)
where
for</*all*/ 'iter> &'iter Collection: IntoIterator<Item = &'iter u8>,
{
self.collection
.into_iter()
.filter(|&&x| x == 5)
.for_each(|x| {
println!("{:?}", x);
});
}
}
-
(I have named the anonymous lifetime 'call_site
, since that is whence Rust picks it).
-
Note: we always have Self : 'call_site
, i.e., all the borrows contained in Self
(if any) outlive (live at least as long as) 'call_site
, or in other words: the 'call_site
borrow of Self
cannot be longer than any borrow contained within Self
.
This is interesting, because in our example Self
is generic over Collection
, and Collection
may be borrowing data:
// This can be our Collection
struct BorrowedBytes<'bytes> {
borrowed_bytes: &'bytes [u8],
}
impl<'bytes, 'iter> IntoIterator
for &'iter BorrowedBytes<'bytes>
where
'bytes : 'iter, // the borrowed bytes must live at least as long as the into_iter() borrow
{
type IntoIter = ::std::slice::Iter<'iter, u8>;
type Item = &'iter u8;
fn into_iter (
self: &'iter BorrowedBytes<'bytes>,
) -> Self::IntoIter
{
self.borrowed_bytes.into_iter()
}
}
The issue here is that @Enet4's HRTB:
fn do_something<'call_site> (self: &'call_site Self)
where
for</*all*/ 'iter> &'iter Collection: IntoIterator<Item = &'iter u8>,
requires that &'iter Collection
be defined for all (unbounded) lifetimes 'iter
:
-
since &'a T
is only (well-)defined when T : 'a
, we get that:
for</*all*/ 'iter> Collection : 'iter
i.e.,
Collection : 'static
i.e.,
Collection
cannot borrow a local (variable).
And indeed if we combine @Enet4's HRTB having an explicit for<'iter> Collection : 'iter
with my BorrowedBytes
struct, we get a compilation error:
error[E0716]: temporary value dropped while borrowed
--> src/main.rs:44:54
|
43 | / MyData {
44 | | collection: BorrowedBytes { borrowed_bytes: &vec![1, 2, 5] },
| | ^^^^^^^^^^^^^ creates a temporary which is freed while still in use
45 | | }
46 | | .do_something()
| |___________________- argument requires that borrow lasts for `'static`
47 | }
| - temporary value is freed at the end of this statement
This is quite saddening, given that we never use that Collection : 'static
(we never really needed it; the bound just came from the overly strong semantics of HRTB (unbounded universal lifetime parameters)).
Solution
Instead of using HRTB's unbounded universal ("for all") lifetime parameters, we can just use our classic bounded lifetime parameters:
impl<Collection> MyData<Collection>
{
fn do_something<'iter, 'call_site : 'iter> (self: &'call_site Self)
where
&'iter Collection: IntoIterator<Item = &'iter u8>,
{
And if we think about it, we can even choose 'iter = 'call_site
:
impl<Collection> MyData<Collection>
{
fn do_something<'call_site> (self: &'call_site Self)
where
&'call_site Collection: IntoIterator<Item = &'call_site u8>,
{