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

RFC for DerefMove #2439

Closed
wants to merge 2 commits into from
Closed
Changes from 1 commit
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
322 changes: 322 additions & 0 deletions text/0000-deref-move.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,322 @@
- Feature Name: `deref_move`
- Start Date: 2018-05-05
- RFC PR: (leave this empty)
- Rust Issue: (leave this empty)

# Summary
[summary]: #summary

Add a new `DerefMove` trait that allows consuming a smart pointer to move its
contents, as in `let x = *p;`.

For more background and discussion, see #178 and #997.

# Motivation
[motivation]: #motivation

Currently, two smart pointer traits provide access to values contained in smart
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nix the first instance of "smart pointer", I think ("two traits provide...")

pointers: `Deref` and `DerefMut`. These traits allow overloading of the
dereferencing (unary `*`) operator, and by extension participate in autoderef in
a number of places, such as method calls and index expressions.

These two traits, however, only dereference references to produce references to
the contained value. They do not offer the option to dereference by value,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sentence is a mouthful. Perhaps:

Unfortunately, these two traits only provide borrowed access to the contained value.

consuming the smart pointer to produce the owned value inside. As a special
case, `Box<T>` has compiler support allowing it to be dereferenced (explicitly
or implicitly) to produce the inner `T`. Because this is special-cased in the
compiler, this functionality is not available for other smart pointer types.

Other smart pointers may support this consuming operation, but it must be done
explicitly due to the lack of language support. For instance, in the stdlib, the
following methods both consume a smart pointer to produce the inner value:

* `ManuallyDrop<T>::into_inner()`
* `binary_heap::PeakMut<'a, T>::pop()`
* `Cow<'a, B>::into_owned()`

I have not made any attempt to survey into the larger ecosystem for more
examples, but there are probably many more.

Note that removing `FnBox` is explicitly *not* part of the motivation for this
RFC. It has been suggested in a few places, such as in discussion of
rust-lang/rust#28796, that `DerefMove` would allow `Box<FnOnce()>` to be called
without needing the `FnBox` workaround. This is not the case, however: the inner
value is dynamically-sized and thus cannot be moved onto the stack. Indeed, one
can call a `Box<T>` where `T` is a concrete type implementing `FnOnce()`.
`DerefMove` would allow `Box<FnOnce()>` to continue not working, just without a
special case in the compiler to allow it to do so.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These last two sentences are jumbled up, and I'm not actually 100% clear what they're trying to express. Maybe just:

Although DerefMove would allow smart pointers containing a concrete FnOnce instance to be called.

And it may be worth noting that if by-value DSTs ever get implemented (which is theoretically possible and has long been desireable), then the combination of that feature with DerefMove would in fact be sufficient to make Box<FnOnce()> work.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By-value DSTs are sufficient and necessary for Box<FnOnce()>, even in absence of DerefMove. As you noted, concrete FnOnce instances can be called with DerefMove and they can be called from a Box today.

I'll try to clarify this.


This will not remove all of `Box`'s special cases from the compiler, but
represents progress towards that goal.

# Guide-level explanation
[guide-level-explanation]: #guide-level-explanation

The following trait is defined in `std::ops`:

```rust
trait DerefMove : DerefMut {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not obvious to me why this should require DerefMut and not just Deref. I can certainly imagine wrapper types that aren't DerefMut because the constructor checked an invariant, but that doesn't need to block DerefMove because once the inner value has been moved out, the invariant is no longer relevant. Example I pulled out of a hat: struct AlignedPtr<T>(*const T); wouldn't be DerefMut, but could be Deref and DerefMove.

Copy link
Contributor

@pythonesque pythonesque May 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I agree. DerefMut -> Deref only makes sense because the caller of deref_mut can always reborrow the returned value to &Target, and worst case the Deref implementation can always be implemented to do the exact same thing. By contrast, DerefMut can't always be implemented by the caller of deref_move, so we don't have DerefMove -> DerefMut.

Actually, the DerefMove -> Deref implication does not work either, in the sense that you can't always implement Deref with a target for which you can implement DerefMove, even if the target is Copy: this is easy to see since you can implement DerefMove with any (sized) type as a target.

There is an implication the other way (Deref -> DerefMove where Target: Copy) so hypothetically the thing you actually want is something like this:

trait DerefMove {
  type Target;
  // ...
}

impl<T> DerefMove for T where T: Deref, <T as Deref>::Target: Copy {
  // We can avoid consuming ourselves *and* fork over the deref target.
  type Target = (T, <T as Deref>::Target);
 // [insert copying implementation using `Deref` on `self`]
}

Actually using this implementation would be irritating, but it gets the idea across (namely, how the Copy special case works). In general we can also implement Deref for a type that we can't move (for instance, by reborrowing a static instance of the type), so of course this implication also fails to hold for non-copy Targets, and this holds for DerefMut as well (e.g. we could be holding a mutable reference to a type for which we have only one instance, so we can't swap or replace it). One could probably argue that scenarios like this are abuses of Deref, DerefMove, or both, but either way it's not a forced decision, and I'm not entirely sure they need to share a target: the strategy is to ignore DerefMove entirely when T: Deref has a Copy target, so requiring them to have the same type doesn't accomplish anything, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rationale is below, but I had not considered this use case. The ergonomic drawbacks may be worth it if this use case is worth supporting.

Copy link
Contributor Author

@alercah alercah May 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The behaviour of ignoring DerefMove when Target : Copy for Deref would be complicated if the Target types were different; that would possibly create scenarios where a generic smart pointer would fail for a non-Copy Target because DerefMove is ignored but Deref has a different Target.

This would also complicate autoderef and in particular its relationship with overload resolution, probably unbearably so. I think it would be so hard to use a type with different Deref and DerefMove Targets that, if we decided to make the two independently implementable, the compiler must enforce that the Targets are identical.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I suppose something like struct TaggedPtr(usize); is a case where it could plausibly be DerefMove but not Deref -- the move version is cheap, just masking away the extra bits in the alignment, and conceptually reasonable, since it's still a pointer -- but it can't be Deref because it doesn't actually have the real pointer to borrow.

That would need that "split a trait into two" RFC, I think, to introduce DerefTarget or something that just provides the associated type. But it might also be too much of a stretch to bother supporting even if we could...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small nit: this trait requires a Sized super trait and where Self::Target: Sized bound.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@scottmcm The TaggedPtr case could still be Deref, I think, since usize is Copy, as long as the target was the pointed-to value, not the pointer itself. It would require unsafe code to cast the masked usize to an actual pointer with a lifetime, but that would require unsafe code anyway--it would still (presumably) be safe.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pythonesque Good point! I forget that pointers get to cheat 🙂

fn deref_move(self) -> Self::Target;
}
```

This trait allows moving out of the result of a derefence. This can happen
either explicitly, as in `let x = *p;`, or implicitly, as in `p.into_foo()`.
`Box<T>` from the standard library implements `DerefMove`, allowing you to move
out of it.

```rust
let b = Box::new("s".to_owned());
let s = *b;
// ERROR: b has already been moved on the previous line.
// let t = *b;
```

If the `Target` type is `Copy`, then `DerefMove` will not be used, and instead
`Deref` will be used and the resulting referenced value will be copied:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think understand why this is necessary after staring at it for a while (since otherwise existing code that expects to be able to copy out of *x would be broken as it would move out of b incorrectly). But as I noted above: it's not clear to me why the Target of DerefMove has to be the same as that for Deref since the rule seems to be that if the target for Deref is Copy we ignore DerefMove. You could always lint for implementations of DerefMove with targets that are known to be Copy at definition time.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This wouldn't be sufficient:

fn foo(b: Box<(String, i32)>) {
    let r = (*b).1; // copy!
    drop(b);
}


```rust
let b = Box::new(0i32);
// Since i32 is Copy, this is equivalent to i = b.deref().
let i = *b;
// That makes this legal, since b was not moved.
let j = *b;
```

# Reference-level explanation
[reference-level-explanation]: #reference-level-explanation

`DerefMove` is a lang item that, like `Deref` and `DerefMut`, plays a role in
dereferencing, both explicitly with the unary `*` operator and implicitly with
autoderef.

`deref_move` is only called in contexts where a value is needed to be moved out.
This can occur either because an explicit dereference is used in a context where
a value is to be moved, or because autoderef selects a method taking `self` (or
`Box<self>`) as a parameter. In both cases, when evaluating whether to call
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forget, is Box<self> magic or would this also work for MyBox<self>? (should this be made explicit?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Box<self> is magic as a receiver type, and I'm not proposing to eliminate that here. It's only mentioned here for completeness.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is sort of getting fixed with arbitrary self types, right? Or is this specifically about the Box<self> syntax?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, once arbitrary receiver types are accepted then Box won't be magical. But that shouldn't affect this RFC one way or another---it's just that I felt I needed to mention that not all by-value receivers are Self. Overload resolution will continue the way it normally is (note: I said "autoderef selects" above, but I really meant overload resolution).

`deref_move`, the compiler will look at whether `Target` implements `Copy`. If
it does not, then `deref_move` is called to move the target out of the smart
pointer. If it does, then instead the compiler will only call `deref` and then
copy out of the returned reference. For generic types, the compiler only checks
if a `Copy` bound is available at the use site; it is not done during
monomorphization.

For explicit dereference, the changes are straightforward. Any explicit
dereference of a `DerefMove` type becomes a movable expression, just as
dereference of `Box` is today.

For autodereference:

* In a function call expression, the callee operand may be dereferenced and
moved if the `Target` implements `FnOnce` and no reference-based call trait.
* In a method call expression, the receiver operand may be dereferenced and
moved if the function selected by overload resolution takes `self` or
`Box<self>` as a parameter (in the latter case, this would require a smart
pointer containing a `Box<Self>`, of course).
* In a field access expression, the left-hand operand may be dereferenced and
moved if the result of the expression is moved. In this case, the move is
always entire; partial move is not supported when a dereference is necessary.
* An index expression can never move its left operand, so it is unaffected.
* A borrow expression can never move its operand, so it is unaffected.

No change is made to overload resolution of method calls. This means that calls
which are invalid today due to preferring `T` over `&T` may now be legal,
selecting to move out of the smart pointer rather than giving an error.

There is an ambiguity in a method call expression when the `Target` type of the
receiver is a reference, and that reference type has a method taking `self`. In
this case, autodereference could either dereference via `deref_move` or via
`deref` or `defer_mut`. In this case, it will prefer the latter (for `deref`, it
is a special case of the `Copy` rule above, but for `deref_mut` it is not) to
avoid unnecessarily moving out of the receiver.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This paragraph could use some more clarification and examples. Why prefer &mut to &? Why not call Deref when the Target type of the receiver is a & and DerefMut when the target type of the receiver is &mut? What does DerefMove have to do with this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it's a bit confusing. If the Target is &T, and there's a self method on &T, then since &T is Copy, the rules above will mean that we use deref and then copy. But with Target = &mut, there are two options: either call deref_mut getting an &mut &mut T and then dereference that, or just call deref_move to get a &mut T; the former should be preferred.

I'll try to clarify this paragraph.


# Drawbacks
[drawbacks]: #drawbacks

## Nobody understands autoderef already and this makes it worse

Or at least, it would if `Box` wasn't already complicating it in the same way.

## Copy behaviour is unintuitive in generic cases

If a type has nontrivial/side-effectful `defer_move`, it may be slightly
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deref_move typo

confusing why it is not used for `Copy` types in non-generic code, but is used
in generic code.

## Breaking change to an edge case of `Box<&mut T>`

Currently, a `Box<&mut T>` variable [can be
dereferenced](https://play.rust-lang.org/?gist=5b3f046ecf29da8f00fde3bd1d2a3df4&version=nightly&mode=debug)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer if you inlined this example, so that these documents are less prone to bitrot.

in at least some cases into a `&mut T` without either requiring that the
variable be mutable (as would be the case for any other `DerefMut` type), or
moving out of the `Box`. This behaviour would not be maintained if its magical
dereferencing were replaced with `DerefMove`, as `DerefMut` requires a mutable
variable.

This is an edge case, as it requires `Box<&mut T>`, which is an unlikely type,
and in most cases it can be trivially fixed by making the variable mutable. It's
possible there are more complex cases (such as if the `Box` itself is stored in
another smart pointer) where this is not easily fixed, but I would be
unsurprised if there were few, if any, instances of this in extant code.
Moreover, the behaviour is arguably a bug because it is taking a mutable borrow
of an immutable variable (and one can modify the above example to get an error
message implying exactly that), though the semantics are more like the unique
immutable borrows used by closures.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed that this feels like a bugfix.


As a result, I propose that this breaking change be accepted after verifying its
effect on Cargo packages.

# Rationale and alternatives
[alternatives]: #alternatives

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A more comprehensive solution like #1646 should be mentioned as an alternative.


## Copy handling

The handling of copies is the real complexity of `DerefMove`. Currently, one can
copy out of a `Box` without consuming it, so this desirable behaviour needs to
be preserved. The proposal is to do so by special casing in the compiler.
However, it could also be done via, for instance, a separate method in the Deref
trait definition:

```rust
fn deref_copy(&self) -> Self::Target where Self::Target : Copy {
return self.deref();
}
```

This still requires special-casing in the compiler, but it is less magical. One
could even envision a `DerefCopy` trait with similar purpose.

These approaches would, however, make diverging implementations (that is,
implementations for which the behaviour of the various deref traits is
unintuitive because they don't actually refer to the same underlying value) more
difficult to write, and we want to avoid that. And they would not fundamentally
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean easier to write?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, sorry. See my above comment that if we did this we may actually need to enforce that they match.

Copy link
Contributor

@pythonesque pythonesque May 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alercah

The behaviour of ignoring DerefMove when Target : Copy for Deref would be complicated if the Target types were different; that would possibly create scenarios where a generic smart pointer would fail for a non-Copy Target because DerefMove is ignored but Deref has a different Target.

Can you elaborate on what you mean by "fail"? I assume all that would happen is that the Deref target and implementation would be used on autoderef, which seems no different than what would happen if the targets were the same.

This would also complicate autoderef and in particular its relationship with overload resolution, probably unbearably so.

Wouldn't it always be unambiguous which implementation to use in any given context, in the same way that Copy itself is? I may be missing something. Or is the problem not about Copy, but that the two types could have methods with the same name, and you couldn't tell which one to call or prioritize one over the other? If that's your concern I definitely see why you would want the types to match.

solve the special casing in the compiler; to do that, we would really need a
more advanced type system allowing us to condition the type of `self` in
`deref_move` on whether or not `Target : Copy`.

With negative trait bounds, we could also bound `DerefMove` on `Target : !Copy`,
eliminating the problem, at the cost of requiring every implementation to also
bound on `!Copy`.

## Status quo

The status quo is disfavoured because it requires special-casing `Box` in the
compiler, privileging it over other smart pointers. While there's no obvious
call in the stdlib for `DerefMut` other than `Box` (see below), it prevents
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: DerefMove

other library authors from writing similar code.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean DerefMove rather than DerefMut?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes, will fix.


## `IndexMove` trait

One option considered in the early discussion was an `IndexMove` trait, similar
to `DerefMove`, which would consume a collection and return a single element. I
have not included this in this RFC because no compelling use case was presented,
and the behaviour of `let s = v[0];` actually consuming `v` seems likely to add
cognitive overhead to the language.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh i just thought of a use-case; arrays. IndexMove is necessary to get e.g. a Box out of an Array. But in that case into_iter or slice patterns are probably better.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, there's definitely a use case for an Index trait which returns things by value. For example, consider a type that wants to expose an API similar to Vec<bool>, but is implemented as a bit set, so indexing into it can't return an existing &bool reference. However, you wouldn't usually want to have self be by value – but afaik that provides the most flexibility, since you could always do impl<'a> DerefMove for &'a MyBitSet. (By itself, this would only allow reading values, not writing them, but #997 also proposed an IndexSet that would support writing.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IndexGet is the intended solution to that problem, but yes it could just be provided through IndexMove.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I initially worked on all versions proposed by #997, but quickly abandoned that when I realize that the IndexGet and IndexSet make more sense as a separate RFC. Note that you couldn't impl DerefMove on a reference since it would have to have the same Target as the provided Deref instance; I don't think that the same restriction would necessarily apply to IndexMove, however.


Compared to other methods of moving out of a collection, `IndexMove` may provide
slight micro-optimization in some cases, but it's not clear without benchmarks
that this is actually meaningfully better than using, say,
`.into_inter().nth(n)`. Given that the `IndexMove` was proposed largely from
parallel construction from `DerefMove` without any specific use cases, it does
not seem worthwhile to move further with it at this time.

## Weakening `DerefMut` bound

The `DerefMut` bound on `DerefMove` and, in particular, the `Deref` bound that
it implies, constrain implementors somewhat. It requires that `Deref` be
implementable, so the implementation is not allowed to overload dereferencing in
such a way that it produces a new value each time. This would not be useful for
non-`Copy` types, since they would be consumed, but a `Copy` type could in this
fashion allow itself to be copied and then produce a new value on each
derefence. This would be useful to avoid redefining methods on the outer type.

This very much goes against the intent of the `Deref` traits to be restricted
smart pointers, however. `Deref` is currently the only way for methods of one
type to be callable on another, but it is deliberately limited in scope. For
instance, one might be tempted to implement `Deref` on a newtype for this
purpose, but this is considered unidiomatic. Additionally, this is a bandaid for
the fact that newtypes would also often like to be able to inherent trait
implementations, and inhereting methods is not sufficient for that.

The additional requirement is to avoid complication of the following case:

```rust
struct RefMethod();

trait Consume : Sized {
fn consume(self) {}
}

impl<'a> Consume for &'a mut RefMethod {}

struct BadDeref<'a> (&'a mut RefMethod);

impl<'a> Deref for BadDeref<'a> {
type Target = RefMethod;
fn deref(&self) -> &Self::Target { &self.0 }
}

impl<'a> DerefMove for BadDeref<'a> {
fn deref_move(self) -> Self::Target { self.0 }
}

fn main() {
let mut r = RefMethod{};
let b = BadDeref(&mut r);
b.consume();
b.consume();
}
```

In this case, the first `b.consume()` is legal, but the second `b.consume()` is
not since `b` was already moved out of. But if `BadDeref` also implements
`DerefMut`, then the first `b.consume()` becomes illegal because `deref_mut`
becomes preferred and `b` is immutable. By requiring that `DerefMove :
DerefMut`, this edge case cannot be encountered: this code will always be
illegal unless `b` is made mutable.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I sort of understand why this is a bad edge case, but as was pointed out above there are useful situations where you can't feasibly implement DerefMut; however, this would still be a bit surprising in those cases. I think what this edge case actually suggests is that Box gets it "right" mostly by accident; if it is important, the rules for mut bindings should be tweaked around DerefMove somehow (possibly that &mut methods on a target of DerefMove should require a mut binding, though I'm not sure how you justify that). In any case, allowing this edge case still seems preferable to me to ruling out DerefMove on !DerefMut types.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The behaviour of Box here is the same as the unique immutable reference that closures use to get around the difficulty of an immutable variable holding a mutable reference, which can be expressed in MIR but not in Rust. We could consider making deref_mut take &uniq self, if that type existed, and then this problem would not exist. Alternatively, simply dropping the notion of immutable variables would also solve this.


# Prior art
[prior-art]: #prior-art

The compiler special case for `Box` is the most compelling prior art. This RFC
is written with that behaviour in mind; one of the most important criteria here
is that `Box` should remain completely backwards-compatible. In case of any
conflict between current `Box` behaviour and the behaviour specified in this
RFC that isn't explicitly called out as being different, it should be considered
a bug in the RFC and either this RFC should be corrected or more discussion
should ensue.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rust allows this:

struct A<T: ?Sized> {
    x: String,
    y: T,
}

fn foo(b: Box<A<dyn Send>>) {
    let r = (*b).x; // explicit deref for clarity
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also moving from b.0 doesn't drop the rest of *b until b is dropped.

struct D(i32);

impl Drop for D {
    fn drop(&mut self) {
        println!("drop({})", self.0);
    }
}

fn main() {
    let mut b = Box::new((D(0), D(1)));
    drop(b.0);
    println!("Hi!");
    b = Box::new((D(2), D(3))); // old value of b is dropped.
    println!("Hi again!");
}

Output:

drop(0)
Hi!
drop(1)
Hi again!
drop(2)
drop(3)


# Unresolved questions
[unresolved]: #unresolved-questions

## Implementors

It's not clear to me which, if any, of the smart pointers above should implement
`DerefMove` other than `Box`. It requires discussion of whether inadvertently
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Vec should likely have an implementation; I expect it can't right now because of sizedness issues. Actually, that suggests to me that allowing by-value DSTs is pretty important to making DerefMove work well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point. I will revise the RFC to discuss the interaction with by-value DSTs more, since that seems very fundamental.

moving out of the pointer could cause issues. I think that it's reasonable to
start with only `Box` for now:

* `Cow` does not implement `DerefMut`, so it is ineligible.
* `PeekMut`'s `pop` has side effects on the containing collection, and so it
seems best to require it to be moved explicitly.
* Moving out of a `ManualDrop` causes the newly-moved value to regain normal
drop semantics. This seems like the least dangerous, given the mechanics of
`ManualDrop`, but it still probably deserves separate consideration.

Additional implementations could, of course, be done as separate proposals for
the libs team.

## Preferring to copy the pointer

Some earlier discussion proposed that when a type implements both `DerefMove`
and `Copy`, then the special case of a `Copy` target does not apply. In other
words, given `let x = *p;`, if both `p` and `p`'s target type are `Copy`, then
rather than calling `deref` and copying the result, this assignment would call
`deref_mut`, copying `p` to use as the receiver.

Because of the above decision to require `DerefMut`, `p` must also be able to
get a mutable reference to its target. If it were `Copy`, then, it almost
certainly owns its target, meaning that copying `p` is going to copy `*p`
anyway. So copying only `*p` is a lesser copy, and simplifies the rules by
eliminating a special case. But we may decide that we want to make `DerefMove` a
bit more general, in which case we might want to consider this behaviour.