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.
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:
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
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 }
}
}
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
}
}