jpernst / rental

Rust macro to generate self-referential structs
Apache License 2.0
211 stars 11 forks source link

How do I use ref_rent() ? #14

Closed MarkSwanson closed 7 years ago

MarkSwanson commented 7 years ago

I'm using rent() just fine, but I can't figure out how to use ref_rent()...

        #[rental]
        pub struct RentContextRead {
            segment: Box<message::Reader<OwnedSegments>>,
            point: point::Reader<'segment>,
        }

*ref_rent() fails: let point: &point::Reader = rent_context.rent(|point| point);

Error messages: the lifetime cannot outlive the lifetime 'segment the lifetime must be valid for the static lifetime...

How would I meet the lifetime requirements?

Do I need to create something like this:

        #[rental]
        pub struct RentContextReadRef {
            segment: Box<message::Reader<OwnedSegments>>,
            point: Box<point::Reader<'segment>>,
            point_ref: &'segment point::Reader<'segment>,
        }

and use point_ref ?

    let rent_context = match rent_point::RentContextReadRef::try_new(
        Box::new(segment),
        |segment| {
            match segment.get_root::<point::Reader>() {
                Ok(point) => Ok(Box::new(point)),
                Err(e) => return Err(ErrorKind::RentContextFailed)
            }
        },
        |point, _| {
            Ok(&point)
        }
    ) {
        Ok(rent_ctx) => rent_ctx,
        Err(e) => panic!("todo: {:?}", e.0)
    };

I must still be doing something wrong because there are still 'static lifetime problems (and other lifetime problems)

Thanks!

MarkSwanson commented 7 years ago

I can replicate the problem using your simple_ref.rs test with the following mods:

1. Give Foo a lifetime
2. do not use deref_suffix

pub struct Foo<'a> {
    i: i32,
}
        #[rental]
        pub struct SimpleRef {
            foo: Box<Foo>,
            iref: &'foo i32,
        }

Try to get back a &Foo using ref_rent()

The problem seems to be the life time on Foo<'a>.

MarkSwanson commented 7 years ago

Ok, I found the lt_params.rs example, so that explains the Foo<'a>.

I'm still stuck working through my problem. Maybe I need another prefix container...

jpernst commented 7 years ago

ref_rent is for reborrowing references tied to a rental lifetime, but this will only work if the TARGET of the reference does NOT contain any rental lifetimes. You can't do what you're trying to do because point contains the rental lifetime 'segment in its type signature. Use of ref_rent is relatively rare, and it's probably not what you want here.

MarkSwanson commented 7 years ago

I've modified your string.rs test to show the problem:

  1. I stopped using deref_suffix because in my real case I can't change the library struct to use Deref
rental! {
    pub mod rent_string {
        #[rental]
        pub struct OwnedStr {
            buffer: String,
            slice: &'buffer str,
        }
    }
}

#[test]
fn read() {
    let buf = "Hello, World!".to_string();
    let rbuf = rent_string::OwnedStr::new(buf, |slice| slice);

    assert_eq!(&*rbuf, "Hello, World!");
}

Question: how would this work without deref_suffix ?

Question: I notice in your tests/generic.rs you assign Foo to be generic over the 'static lifetime.

It seems ... like this isn't the case for any real program right? Because no real program would place Foo in the 'static lifetime???

jpernst commented 7 years ago

deref_suffix uses ref_rent internally to implement it. For the string example, just do:

assert_eq!(rbuf.ref_rent(|suffix| &**suffix), "Hello, World!");

As for the static bound in the generics test, that's a current limitation of rust. I can't lift that bound until rust gets the 'unsafe lifetime.

MarkSwanson commented 7 years ago

So... it seems like I have no way to do what I need to do. I simply want to get a &str by calling point.name() (the returned string would have the same lifetime as the rental struct (or, the same lifetime as 'point) (the same way the string.rs example returns a &str)

Ideas?

Maybe I can just transmute the string I get back to have the same lifetime as the rental struct?

MarkSwanson commented 7 years ago

Wrt the TARGET lifetime, the string.rs example has a TARGET of a reference that has a rental lifetime ('buffer).

    let s = rbuf.ref_rent(|suffix| &**suffix);

s has a 'buffer lifetime (right? defined in slice: &'buffer str) ?

jpernst commented 7 years ago

You can do what you're trying to do:

let name = my_rental.ref_rent(|point| point.name())

And no, the rental lifetime is only attached to the reference itself. We reborrow the reference into another reference with a shorter lifetime. When I say the target of the reference, I mean the Deref::Target type, which in this case is simply str. str has no lifetime parameters at all and is therefore 'static.

MarkSwanson commented 7 years ago

That works! Brilliant!

My problem was that I was trying to get the closure to return a reference to point, and then get the name outside the closure.

Thanks for explaining about str and 'static - I was incorrectly reading that the whole time.