-
Notifications
You must be signed in to change notification settings - Fork 6
Scratchpad on PropertyMaps
Access is in the form fn get($self, handle: Handle) -> $ret
where $self
is one of self
, &self
and &mut self
and $ret
one of P
, &P
and &mut P
(where the references have the same lifetime as $self
references!). This leaves us with 9 possibilities in theory:
-
self -> &P
- Not possible: since
self
then lives in the stack of the function, we cannot return references to self.
- Not possible: since
-
self -> &mut P
- Not possible: see above.
-
&self -> &mut P
- This is interior mutability. It's usually not implementable like this (because then the caller could make the mut ref live forever without the collection knowing about it). The only way I can imagine implementing this is by allowing only a "one-shot borrow". Where the first call succeeds and all of the following panic. Not very useful.
-
self -> P
- As we are talking about collections, consuming self doesn't really make a whole lot of sense. It's like
Vec<T> -> T
... useful in only a few rare situations.
- As we are talking about collections, consuming self doesn't really make a whole lot of sense. It's like
-
&mut self -> &P
- If
&mut self
is required to create a&P
, this means that there is some non-semantic mutability at play. Like ... caching or something. In those cases, usually interior mutability should be used. So we can ignore this (as do many of thestd
traits, likeIndex
).
- If
-
&self -> P
- Useful e.g. for mappings where from one value a new one is created. This new one doesn't live anywhere in the collection.
-
&self -> &P
- Very useful, like
Index
- Very useful, like
-
&mut self -> &mut P
- Very useful, like
IndexMut
- Very useful, like
&mut self -> P
Without GATs, it's not very useful to have a &self -> P
trait, because such a trait would be unrelated to a &self -> &P
trait. With GATs we would like to have something like this:
trait PropMap<H: Handle> {
type Property<'a>;
fn get(&self) -> Self::Property;
}
trait PropIndex<H: Handle>
: Index<H> + for<'a> PropMap<H, Property = &'a <Self as Index<H>>::Output>
{}
Meaning: PropMap
is a super trait of PropIndex
. This in turn means that many functions can bound their maps with PropMap
(because they don't care if they get a reference or an owned value).
But again, without GATs, it's not really nice to implement this. It can be done something like this, but this spills lifetimes everywhere.
So for now, we will only use the two trait analogous to ops::Index
and ops::IndexMut
. This makes a few pattern impossible (like a nice Mapping which doesn't just use subreferences), but it's a sensible start for now and works for most cases.
Two possibilities:
- The handle kind becomes an input type for the trait. Then types could implement
PropMap<EdgeHandle>
andPropMap<FaceHandle>
. Problem: using the trait as parameter might be more difficult:impl PropMap<impl FaceHandle>
or something like that? - The handle kind becomes an output type for the trait. That way, a specific type can only be indexed by one specific handle type. That would have more restrictions, but one could work around this like
mesh.face_props()
.
The "handle kind" should probably be generic over the HandleIndex
used. So something that can be indexed by FaceHandle<u32>
should also be indexable by FaceHandle<u16>
and FaceHandle<u64>
. This looks like one could use the family pattern here. However, this requires GATs.
This could make the API way easier when we can't use GATs. And using indices other than u32
is an edge case anyway.
trait Props<H: Handle> {
type Output;
}
PropMap
Props
PropStore