I need a trait that allows me to construct a object that borrows an object that borrows something. In the following example that is PaperBin. https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=78fb3f88b71bc226614912001ceca65b
trait GarbageBin<'a,'b>{
fn new(rubbish: &'b Paper<'a>) -> Self;
}
struct PaperBin<'a,'b> {
rubbish: &'b Paper<'a>
}
struct Paper<'a> {
matter: &'a [u8]
}
impl<'a,'b> GarbageBin<'a,'b> for PaperBin<'a,'b> {
fn new(rubbish: &'b Paper<'a>) -> Self{
Self {
rubbish: rubbish
}
}
}
fn create_bin_with_rubbish<'a,'b, T>()
where T: GarbageBin<'a,'b>
{
let matter = &[1][..];
let rubbish = Paper{matter};
This gives an error:
//let garbage_bin = T::new(&rubbish);
}
#[test]
fn run() {
create_bin_with_rubbish::<PaperBin>();
}
I need to create GarbageBins generically determined on a function call, as in the example. This example looks maybe a unnecessary regarding the fixed type Paper in the new() associated function of the Trait. This should become a Trait object in the real code.
How can I realize the creation of garbage_bin the generic type T?
here is the error message that I get, if I un-comment the line:
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
--> src\reference_to_reference\mod.rs:23:23
|
23 | let garbage_bin = T::new(&rubbish);
| ^^^^^^
|
note: first, the lifetime cannot outlive the lifetime `'a` as defined on the function body at 18:28...
--> src\reference_to_reference\mod.rs:18:28
|
18 | fn create_bin_with_rubbish<'a,'b, T>()
| ^^
note: ...but the lifetime must also be valid for the lifetime `'b` as defined on the function body at 18:31...
--> src\reference_to_reference\mod.rs:18:31
|
18 | fn create_bin_with_rubbish<'a,'b, T>()
| ^^
note: ...so that the types are compatible
--> src\reference_to_reference\mod.rs:23:23
|
23 | let garbage_bin = T::new(&rubbish);
| ^^^^^^
= note: expected `GarbageBin<'_, '_>`
found `GarbageBin<'a, 'b>`
Your immediate issue is that T: GarbageBin<'a, 'b>
where 'a
and 'b
are parameters of create_bin_with_rubbish
and must therefore outlive calls to that function—but the actual lifetimes passed to T::new
are only internal to the function and do not therefore satisfy those bounds.
Instead of parameterising create_bin_with_rubbish
with lifetimes 'a
and 'b
, one way to resolve this would be to use instead an HRTB (higher-ranked trait bound):
fn create_bin_with_rubbish<T>()
where
T: for<'a, 'b> GarbageBin<'a, 'b>,
However, PaperBin
implicitly requires that 'a: 'b
, and such bounds are not currently supported in HRTBs. But this in itself is indicative of the second lifetime parameter being unnecessary: remember, a lifetime parameter only means "lives at least as long as..." and multiple instances of the same parameter can represent different concrete lifetimes (so long as they each satisfy that same bound). So let's simplify:
struct PaperBin<'a> {
rubbish: &'a Paper<'a>,
}
Finally, using the HRTB does require breaking the immediate link between the lifetimes of GarbageBin
and its implementor PaperBin
—but that makes sense, because you're trying to pass in the PaperBin
type without knowing its lifetime parameters and are then invoking GarbageBin
's constructor method upon it to obtain a PaperBin
with specific lifetime parameters. So now we implement GarbageBin<'a> for PaperBin<'_>
—but we still need the constructor to return an instance of PaperBin<'a>
, for which we add an associated type:
trait GarbageBin<'a> {
type Constructed: GarbageBin<'a>;
fn new(rubbish: &'a Paper<'a>) -> Self::Constructed;
}
struct Paper<'a> {
matter: &'a [u8]
}
struct PaperBin<'a> {
rubbish: &'a Paper<'a>
}
impl<'a> GarbageBin<'a> for PaperBin<'_> {
type Constructed = PaperBin<'a>;
fn new(rubbish: &'a Paper<'a>) -> PaperBin<'a> {
PaperBin { rubbish }
}
}
fn create_bin_with_rubbish<T>()
where
T: for<'a> GarbageBin<'a>,
{
let matter = &[1][..];
let rubbish = Paper { matter };
let garbage_bin = T::new(&rubbish);
}
fn main() {
create_bin_with_rubbish::<PaperBin>();
}
Using a GAT (generic associated type), which is currently an unstable feature, you can push the lifetime parameter from the trait into the associated type, which negates the need for an HRTB:
#![feature(generic_associated_types)]
trait GarbageBin {
type Constructed<'a>: GarbageBin;
fn new<'a>(rubbish: &'a Paper<'a>) -> Self::Constructed<'a>;
}
struct Paper<'a> {
matter: &'a [u8]
}
struct PaperBin<'a> {
rubbish: &'a Paper<'a>
}
impl GarbageBin for PaperBin<'_> {
type Constructed<'a> = PaperBin<'a>;
fn new<'a>(rubbish: &'a Paper<'a>) -> PaperBin<'a> {
PaperBin { rubbish }
}
}
fn create_bin_with_rubbish<T: GarbageBin>() {
let matter = &[1][..];
let rubbish = Paper { matter };
let garbage_bin = T::new(&rubbish);
}
fn main() {
create_bin_with_rubbish::<PaperBin>();
}