Moves from `self` during the drop hook

I'm not sure I understand what you're asking. I'm certainly not saying that all drop implementations should be guaranteed not to move memory around, only that it should be possible to implement drop for a type that can't be moved. One would obviously be very limited as to when and how one could use such a type. That's why I'm suggesting that everything would have a Move bound by default, similarly to how Sized works. A generic struct or function that wanted to use ptr::read obviously wouldn't specify : ?Move.

@pnkfelix The way I see it is that there is no real problem with an infinitely recurring drop impl, even with linear types (c.f. loop { } : ⊄). The warning exists not to prevent doing such a thing, but to prevent doing these things by mistake. As such, it is fine for it to be sloppy/overzealous (e.g. “You are dropping T in impl of Drop for T here, you might want to make sure that is intentional”). Moreover the warning can be tweaked over time without any effect on backwards compatibility.

@eddyb I would be less interested in &move if it was just needed for drop, but I feel Rust, as a systems language, really deserves to be more aware of uninitialized memory (eventually), and once you do try to give it that awareness, &out and &in/&move really demand to exist.

Under a privacy approach, the idea would be that if one can see all the fields in a type, they can effectively move it by dropping and rebuilding it. &move in the context of non-movable types is important to make a clean story on “unprivileged” code that cannot see all the fields being able to call out to a “privileged” function that consumes/moves the non-movable type. With a drop using &move, unless the drop impl wants to wipe the memory or something, that self is passed by reference and not possible by value is besides the point, and perhaps an undesirable performance hit.

1 Like

What about just letting std::mem::forget() enable those semantics?

struct Foo {
  x: Box<u32>,
}
impl Drop for Foo { ... }

fn f1() -> Box<u32> {
  let foo = Foo { x: Box::new(0u32) };
  // would not compile, the whole `foo` needs to move into the Drop glue.
  foo.x
}
fn f2() -> Box<u32> {
  let foo = Foo { x: Box::new(0u32) };
  let rv = foo.x;
  // prevent the drop glue from being inserted for foo, so partial moves from
  // foo are allowed.
  unsafe { std::mem::forget(foo) };
  rv
}

The idea is that the drop glue can be treated as a consumer of the struct, so that it's the move into the drop-glue that prevents partial moves out, rather than it being the fact of Drop being implemented... You could have partial moves out iff the drop glue is prevented from execution.

I think this is backwards-compatible with today's forget definition: if you don't have any partial-moves out (which you can't, today), then you get today's behavior. Thoughts?

If drop does not get called on destructing, there should be no problem with this.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.