-
Notifications
You must be signed in to change notification settings - Fork 16
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
New projection macro #20
Comments
A similar idea I had was to provide a derive that would create a projection struct covering all the fields, e.g. taking the same example: #[derive(Debug, UnsafePinProject)]
#[must_use = "Futures do nothing unless polled"]
pub struct CancelableFuture<A, B> {
state: Arc<CancelableFutureState>,
#[project(pin)]
future: Option<A>,
when_cancelled: Option<B>,
} would give something like pub struct CancelableFuture<A, B> {
state: Arc<CancelableFutureState>,
future: Option<A>,
when_cancelled: Option<B>,
}
struct CancelableFutureProjection<'a, A, B> {
state: &'a mut Arc<CancelableFutureState>,
future: Pin<&'a mut Option<A>>,
when_cancelled: &'a mut Option<B>,
}
impl<A, B> CancelableFuture<A, B> {
fn project<'a>(self: Pin<&'a mut Self>) -> CancelableFutureProjection<'a, A, B> {
let this = unsafe { Pin::get_unchecked_mut(self) };
CancelableFutureProjection {
state: &mut this.state,
future: unsafe { Pin::new_unchecked(&mut this.future) },
when_cancelled: &mut this.when_cancelled,
}
} (having |
@Nemo157 Yeah, I also thought about something like that, but Of course there's nothing wrong with having both. |
I wrote an attribute macro based on @Nemo157's idea: pin-project It can be written the same example as follows: #[unsafe_project]
#[derive(Debug)]
#[must_use = "Futures do nothing unless polled"]
pub struct CancelableFuture<A, B> {
state: Arc<CancelableFutureState>,
#[pin]
future: Option<A>,
when_cancelled: Option<B>,
} |
There are two reasons I prefer a type level macro for this instead of an expression level one:
|
I don't think that's quite correct. Consider the above That constraint is provided by the However, I agree that a proc-macro or derive solution is nicer (and more intuitive?). So I'm fine with pushing on that as well. Just so long as there's some sort of solution for this (since right now there's none). As a side note, @taiki-e 's implementation uses That makes me uncomfortable, since projecting from So I think the defaults should be reversed: it should pin by default, and require a
This is a good argument in favor of a derive/proc-macro solution. |
As long as you never pin-project to
As long as you never project to a pinned reference to the field (or otherwise assume the field is pinned) then projecting to an unpinned reference is safe. Also, pin-projecting to a field with the wrong conditional |
It can reduce items on safety that human should ensure, so it seems preferable to add this option. #[unsafe_project(Unpin)]
struct Foo<T, U> {
#[pin]
future: T,
field: U,
}
// Automatically create the appropriate Unpin implementation.
// impl<T, U> Unpin for Foo<T, U> where T: Unpin {} // Conditional Unpin impl |
I believe this issue can be closed because the current pin-project is safe. Also, projection macros currently provided by pin-utils will be deprecated in favor of pin-project and pin-project-lite (#29). |
I have used pin-utils extensively on a Pin-heavy library.
Overall I found it rather unergonomic, so I created a new macro:
Here is an example of using it:
Basically, you pass in something which is pinned (in this case
self
), and then it will destructure the individual fields (which are marked as eitherpin
ormut
).Using
pin
is similar tounsafe_pinned
and usingmut
is similar tounsafe_unpinned
.Here is an example of real code using the pin-utils macros (
unsafe_pinned
andunsafe_unpinned
):And the same code with my
unsafe_project
macro:There are three huge advantages to this:
It's overall much shorter. Rather than using
self.as_mut().future()
you just usefuture
. This makes working with pinned structs almost as convenient as unpinned structs!unsafe_pinned
andunsafe_unpinned
require you to specify the bounds/types of the fields twice. But withunsafe_project
you don't need to.It fully supports disjoint borrows, unlike
unsafe_pinned
andunsafe_unpinned
.This allowed me to take this horrible code... and replace it with this lovely code.
In addition, there are a couple places where I need disjoint borrows, like here. Using
unsafe_project
works great, but I cannot useunsafe_pinned
andunsafe_unpinned
for this.I ported my entire codebase to use
unsafe_project
, overall I'm very happy with it.The only downside I found is that you can't call other
self
methods at the same time as usingunsafe_project
:Are you interested in adding in a macro like
unsafe_project
into pin-utils?The text was updated successfully, but these errors were encountered: