How to return reference from newtype taking local ownership?

I wanted to clean up my code a bit by consolidating common code under a trait but since the type is foreign I created a newtype (bevy_reflect):

pub struct Entity<'a>(&'a dyn Struct);

I then created a simple try_from to clean up a lot of boilerplate, but when I try to return a reference owned by Entity, it's a compilation failure. The same is not true if I don't use the newtype at all:

    fn value(&'a self, name: &'a str) -> Result<&dyn bevy_reflect::List, &str>
    {
        let aa: &'a dyn PartialReflect = self.field_ref(name).unwrap();
        let attr: Entity<'a> = Entity::try_from(aa).unwrap();

        // this works, but at cost of extra boilerplate
        // let attr = aa.reflect_ref().as_struct().map_err(|_| "no struct")?;

        attr
            .field_ref("value").unwrap()
            .reflect_ref()
            .as_list()
            .map_err(|_| "not a list")
    }
error[E0515]: cannot return value referencing local variable `attr`
   --> src/test/tmp.rs:195:9
    |
195 |           attr
    |           ^---
    |           |
    |  _________`attr` is borrowed here
    | |
196 | |             .field_ref("value").unwrap()
197 | |             .reflect_ref()
198 | |             .as_list()
199 | |             .map_err(|_| "not a list")
    | |______________________________________^ returns a value referencing data owned by the current function

So I'm hoping I'm wrong that there's a trade-off between newtypes and the side-effects caused by their ownership?

To sum it up, I only want the newtype to provide extension methods to foreign types. I do not want it taking ownership like this otherwise.

Do the conversion without temporary variables.

Thanks, how do you mean?

Looks like you have impl Deref for Entity<'_>? Utilizing that deref will take a local borrow of attr. Try:

        //  vv
        attr.0
            .field_ref("value").unwrap()
            .reflect_ref()
            .as_list()
            .map_err(|_| "not a list")

You're right, thank you. However, field_ref I was hoping would eliminate this boilerplate and with your suggestion I can't seem to use it as it is part of the Entity impl:

fn field_ref(&self, name: &str) -> Result<&dyn PartialReflect, &str> {
        Ok(self
            .0
            .field(name)
            .ok_or_else(|| "missing")?)
    }

But if I call attr.0.field(..) directly instead of calling field_ref the compiler is happy.

Why doesn't field_ref cause a local borrow? Doesn't &self still cause the borrow to be local to the caller?

The problem is there's no way to emulate reference-based borrow checking with the Deref trait. Here's the signature of deref:

fn deref<'de>(&'de self) -> &'de Self::Target

So if you have a Entity<'a>, you couldn't use deref to get a &'a dyn Struct unless you could borrow Entity<'a> itself for 'a, but that's not possible -- you can never borrow a local for a non-local lifetime like 'a. So you end up with

Deref::deref(&'local Entity<'a>).field_ref("value")...
// =>
(&'local dyn Struct).field_ref("value")...

But you can make calls through and reborrow through a &'a dyn Struct without borrowing the reference itself.


OK, so what are your choices if you want to try to improve the situation?

  • Use a function instead of a newtype and work with &dyn Struct directly
  • Get rid of Deref and implement all the desired functionality on Entity directly (via dispatch)
  • Use a custom DST which involves a bit of unsafe

For the last I mean something like

#[repr(transparent)]
pub struct Entity(dyn Struct);

// I guess you want `TryFrom` actually but I'm too lazy
impl<'a> From<&'a dyn PartialReflect> for &'a Entity {
    fn from(pr: &'a dyn PartialReflect) -> Self {
        let attr: &'a dyn Struct = aa.reflect_ref().as_struct().unwrap();
        let ptr = attr as *const dyn Struct as *const Entity;
        // Safety: We just created the `ptr` from a `&'a dyn Struct` and
        // `Entity` is `repr(transparent)`
        unsafe { &*ptr }
    }
}
1 Like

I just realized there's a couple pieces missing but I have to go AFK; in short you'd probably want

#[repr(transparent)]
pub struct Entity<'a>(dyn Struct + 'a);

impl<'a> From<&'a dyn PartialReflect> for &'a Entity<'a> {

// And you also need
impl<'a> Deref for Entity<'a> {
    type Target = dyn Struct + 'a;
    fn deref(&self) -> &(dyn Struct + 'a) {

Thank you - I really gotta read up on Deref more to fully absorb your answer. It's moments like these I remind myself of how utter lost I would be without Rust's safety.

I'm fine just using functions as I understand dispatch adds a bit of overhead. Thank you.

That's not strictly true in this case, because we do have &'a dyn Struct, and we can have Deref::deref return a double reference. Method resolution will then be able to resolve past the outer reference and make calls with &'a self through the local:

impl<'a> std::ops::Deref for Entity<'a> {
    type Target = &'a dyn Struct;
    fn deref(&self)->&&'a dyn Struct {
        &self.0
    }
}

(Playground)

1 Like

Ah, nice. I'd use this over the unsafe if it meets your needs.