-
Notifications
You must be signed in to change notification settings - Fork 183
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add yoke crate #705
Add yoke crate #705
Conversation
Codecov Report
@@ Coverage Diff @@
## main #705 +/- ##
==========================================
+ Coverage 74.18% 74.69% +0.50%
==========================================
Files 171 176 +5
Lines 10802 10712 -90
==========================================
- Hits 8014 8001 -13
+ Misses 2788 2711 -77
Continue to review full report at Codecov.
|
Pull Request Test Coverage Report for Build bfae49cd313f5f6272658048f463e61c0484060d-PR-705
💛 - Coveralls |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Manish asked me to perform an unsafe code review. I am not confident this is correct yet, so I've asked for some language clarifications to make sure my understanding is sound.
/// This enforces that while `F` may mutate `Self<'a>`, it can only mutate it in a way that does | ||
/// not insert additional references. For example, `F` may call `to_owned()` on a `Cow` and mutate it, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure? What if it accesses a thread-local?
I don't believe this is an issue but it's worth explicitly checking this case. (Also, I wonder if there is evil to be done with global mutices...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can't borrow from a thread local or a global mutex, you can only do scoped operations on them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, what about
let empty: &'static mut [u8] = &mut [];
Similarly, Box::leak
? I don't think these should be an issue either, because those are truly 'static
and that lifetime is covariant. Just trying to nail down every source of borrowed data I can think of.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, those are truly 'static
. And static mut is unsafe to work with anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I think I'm mostly? confident that this is ok. I haven't managed to think of a way to break out of the "sandbox" so to speak. I'd like to see what Tyler says before formally calling this safe.
utils/yoke/src/cart.rs
Outdated
/// is never accessed via `&mut` references. | ||
/// | ||
/// For example, `Rc<Vec<u8>>` and `Rc<[u8]>` are valid [`Cart`]s, however `Rc<RefCell<Vec<u8>>>` is | ||
/// not, because in the latter type it is possible to use interior mutation to change the data. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, again to clarify, this sounds very strongly like a "pinned as long as a &T
is reachable", but I'm not certain that's what you're saying.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pinned as long as &T::Inner is reachable, yes.
utils/yoke/src/cart.rs
Outdated
/// In general, this means "no interior mutability", though interior mutability can be fine | ||
/// if the interior mutability is to something that does not affect references. For example, | ||
/// `Rc<[u8]>` does have interior mutability in the refcount, but you cannot get references | ||
/// to it, so it's fine. On the other hand, `Weak<Box<[u8]>>` cannot be a valid [`Cart`] because | ||
/// it is possible for the backing buffer to be cleaned up without warning. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, and then this means that as a corollary, Rc<Vec<RefCell<u8>>>
is ok but the transposition Rc<RefCell<Vec<u8>>
is not, because you might cause the Vec's pointee address to change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually thinking about it more both are probably okay: if you have a refcell you can't take longer-lived references to its contents anyway. Neither are useful types since you can't zero copy deserialize (or whatever) from either.
The goal is basically avoiding this pattern:
yoke.attach_to_cart(cart, |data| { do_something_that_borrows_from_data(data) });
yoke.backing_cart().mutate_the_borrowed_thing();
the danger is that I'm not 100% sure there's no way to use interior mutability here where the lifetime escapes, because we are casting the lifetime a bunch of times.
/// This enforces that while `F` may mutate `Self<'a>`, it can only mutate it in a way that does | ||
/// not insert additional references. For example, `F` may call `to_owned()` on a `Cow` and mutate it, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, what about
let empty: &'static mut [u8] = &mut [];
Similarly, Box::leak
? I don't think these should be an issue either, because those are truly 'static
and that lifetime is covariant. Just trying to nail down every source of borrowed data I can think of.
utils/yoke/src/yoke.rs
Outdated
} | ||
} | ||
|
||
// clone impls only work for reference counted objects, otherwise you should be |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: use CloneStableDeref
here to make this a bit more general. It works for Rc, Arc, and &'a T
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, so this works, but then you can no longer have the impl on Option<Rc<T>>
(since stable_deref_trait
could potentially introduce a impl CloneStableDeref for Option<T>
-- it wouldn't make sense but the compiler doesn't know that)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, can you at least add the missing impls? Consider using macro_rules
to make it shorter.
Co-authored-by: Shane F. Carr <[email protected]>
retriggering docs |
This implements the data structure described in #667 (comment)
The
Yoke
abstraction allows us to "yoke"Cow
s and similar types (ZeroVecs, and data structures using Cow/ZeroVec) to their backing storage, allowing us to turn the lifetimes into dynamic ones that can be managed byRc
s and caches.Carried over from #696 because my WIP force pushes made the bot leave a million comments.