Skip to content
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

Implement project_replace method. #194

Merged
merged 3 commits into from
May 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 75 additions & 2 deletions pin-project-internal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ mod project;

use proc_macro::TokenStream;

use utils::{Immutable, Mutable};
use utils::{Immutable, Mutable, Owned};

/// An attribute that creates a projection struct covering all the fields.
///
Expand Down Expand Up @@ -226,6 +226,63 @@ use utils::{Immutable, Mutable};
///
/// See also [`pinned_drop`] attribute.
///
/// ### `project_replace()`
///
/// In addition to the `project()` and `project_ref()` methods which are always
/// provided when you use the `#[pin_project]` attribute, there is a third method,
/// `project_replace()` which can be useful in some situations. It is equivalent
/// to [`Pin::set`], except that the unpinned fields are moved and returned,
/// instead of being dropped in-place.
///
/// ```
/// # #[rustversion::since(1.36)]
/// # fn dox() {
/// # use std::pin::Pin;
/// # type ProjectionOwned = ();
/// # trait Dox {
/// fn project_replace(self: Pin<&mut Self>, other: Self) -> ProjectionOwned;
/// # }
/// # }
/// ```
///
/// The `ProjectionOwned` type is identical to the `Self` type, except that
/// all pinned fields have been replaced by equivalent `PhantomData` types.
///
/// This method is opt-in, because it is only supported for `Sized` types, and
/// because it is incompatible with the `#[pinned_drop]` attribute described
/// above. It can be enabled by using `#[pin_project(Replace)]`.
///
/// For example:
///
/// ```rust
/// use pin_project::{pin_project, project_replace};
///
/// #[pin_project(Replace)]
/// pub enum Foo<T> {
/// A {
/// #[pin]
/// pinned_field: i32,
/// unpinned_field: T,
/// },
/// B,
/// }
///
/// #[project_replace]
/// fn main() {
/// let mut x = Box::pin(Foo::A { pinned_field: 42, unpinned_field: "hello" });
///
/// #[project_replace]
/// match x.as_mut().project_replace(Foo::B) {
/// Foo::A { unpinned_field, .. } => assert_eq!(unpinned_field, "hello"),
/// Foo::B => unreachable!(),
/// }
/// }
/// ```
///
/// The [`project_replace`] attributes are necessary whenever destructuring the return
/// type of `project_replace()`, and work in exactly the same way as the
/// [`project`] and [`project_ref`] attributes.
///
/// ## Supported Items
///
/// The current pin-project supports the following types of items.
Expand Down Expand Up @@ -320,6 +377,7 @@ use utils::{Immutable, Mutable};
/// [`UnsafeUnpin`]: https://docs.rs/pin-project/0.4/pin_project/trait.UnsafeUnpin.html
/// [`project`]: ./attr.project.html
/// [`project_ref`]: ./attr.project_ref.html
/// [`project_replace`]: ./attr.project_replace.html
/// [`pinned_drop`]: ./attr.pinned_drop.html
#[proc_macro_attribute]
pub fn pin_project(args: TokenStream, input: TokenStream) -> TokenStream {
Expand Down Expand Up @@ -520,7 +578,7 @@ pub fn project(args: TokenStream, input: TokenStream) -> TokenStream {
/// `project_ref` method.
///
/// This is the same as [`project`] attribute except it refers to the projected
/// type returned by `project_ref` method.
/// type returned by the `project_ref` method.
///
/// See [`project`] attribute for more details.
///
Expand All @@ -531,6 +589,21 @@ pub fn project_ref(args: TokenStream, input: TokenStream) -> TokenStream {
project::attribute(&args.into(), input, Immutable).into()
}

/// An attribute to provide way to refer to the projected type returned by
/// `project_replace` method.
///
/// This is the same as [`project`] attribute except it refers to the projected
/// type returned by the `project_replace` method.
///
/// See [`project`] attribute for more details.
///
/// [`project`]: ./attr.project.html
#[proc_macro_attribute]
pub fn project_replace(args: TokenStream, input: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(input);
project::attribute(&args.into(), input, Owned).into()
}

/// An internal helper macro.
#[doc(hidden)]
#[proc_macro_derive(__PinProjectInternalDerive, attributes(pin))]
Expand Down
Loading