From c9ebd816897c436b3caa4d7cba03c8c85b9edada Mon Sep 17 00:00:00 2001 From: John Hodge Date: Thu, 12 May 2016 11:48:42 +0800 Subject: [PATCH 1/6] move-pointer - First draft with rough outline --- text/0000-move-pointer.md | 51 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 text/0000-move-pointer.md diff --git a/text/0000-move-pointer.md b/text/0000-move-pointer.md new file mode 100644 index 00000000000..b1363d6b645 --- /dev/null +++ b/text/0000-move-pointer.md @@ -0,0 +1,51 @@ +- Feature Name: `move_pointer` +- Start Date: 2016-05-12 +- RFC PR: (leave this empty) +- Rust Issue: (leave this empty) + +# Summary +[summary]: #summary + +Introduce a new pointer type `&move` that logically owns the pointed-to data, but does not control the backing memory. Also introduces the DerefMove trait to provide access to such a pointer. + +# Motivation +[motivation]: #motivation + +This provides an elegant solution to passing DSTs by value, allowing `Box` to work. It also will allow other usecases where trait methods should take self by "value". + +# Detailed design +[design]: #detailed-design + +- Add a new pointer type `&move T` +- Add a new unary operation `&move `. With a special case such that `&move |x| x` parses as a move closure, requiring parentheses to parse as an owned pointer to a closure. +- Add a new operator trait `DerefMove` to allow smart pointer types to return owned pointers to their contained data. + - ```rust +trait DerefMove: DerefMut +{ + /// Return an owned pointer to inner data + fn deref_move(&mut self) -> &move Self::Target; + /// Drop self without calling destructor for Self::Target + fn deallocate(self); +} +``` + + +When an owned pointer is created to a variable (e.g. on the stack) the owner of the pointer takes responsability of calling the destructor on the pointed-to data (and can do any operation assuming that it has full ownership of the object). The original owner still controls the memory allocation used to hold the type, and will deallocate that memory in the same way as if the object had been passed by value. + +# Drawbacks +[drawbacks]: #drawbacks + +- Adding a new pointer type to the language is a large change + +# Alternatives +[alternatives]: #alternatives + +- Previous discussions have used `&own` as the pointer type + - Since `own` is not a reserved word, such a change would be breaking. + +# Unresolved questions +[unresolved]: #unresolved-questions + +- `IndexMove` trait to handle moving out of collection types in a similar way to `DerefMove` +- Should (can?) the `box` destructuring pattern be implemented using `DerefMove`? + From 10ae3c2231814183b1b1938b9e6417b6adcb9857 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Thu, 12 May 2016 12:54:07 +0800 Subject: [PATCH 2/6] move-pointer - Add some changes from IRC --- text/0000-move-pointer.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/text/0000-move-pointer.md b/text/0000-move-pointer.md index b1363d6b645..9ce77a1ea89 100644 --- a/text/0000-move-pointer.md +++ b/text/0000-move-pointer.md @@ -17,9 +17,14 @@ This provides an elegant solution to passing DSTs by value, allowing `Box`. With a special case such that `&move |x| x` parses as a move closure, requiring parentheses to parse as an owned pointer to a closure. + - This precedence can be implemented by ignoring the `move` when parsing unary operators if it is followed by a `|` or `||` token. - Add a new operator trait `DerefMove` to allow smart pointer types to return owned pointers to their contained data. - - ```rust + +```rust trait DerefMove: DerefMut { /// Return an owned pointer to inner data @@ -29,7 +34,6 @@ trait DerefMove: DerefMut } ``` - When an owned pointer is created to a variable (e.g. on the stack) the owner of the pointer takes responsability of calling the destructor on the pointed-to data (and can do any operation assuming that it has full ownership of the object). The original owner still controls the memory allocation used to hold the type, and will deallocate that memory in the same way as if the object had been passed by value. # Drawbacks @@ -48,4 +52,5 @@ When an owned pointer is created to a variable (e.g. on the stack) the owner of - `IndexMove` trait to handle moving out of collection types in a similar way to `DerefMove` - Should (can?) the `box` destructuring pattern be implemented using `DerefMove`? +- Potential interactions of what happens when a `&move` is stored From e4b689810295a658544dbd4e269cec4eb546355e Mon Sep 17 00:00:00 2001 From: John Hodge Date: Thu, 12 May 2016 13:06:55 +0800 Subject: [PATCH 3/6] move_pointer - Not about mutation --- text/0000-move-pointer.md | 1 + 1 file changed, 1 insertion(+) diff --git a/text/0000-move-pointer.md b/text/0000-move-pointer.md index 9ce77a1ea89..93f62869f17 100644 --- a/text/0000-move-pointer.md +++ b/text/0000-move-pointer.md @@ -53,4 +53,5 @@ When an owned pointer is created to a variable (e.g. on the stack) the owner of - `IndexMove` trait to handle moving out of collection types in a similar way to `DerefMove` - Should (can?) the `box` destructuring pattern be implemented using `DerefMove`? - Potential interactions of what happens when a `&move` is stored +- Should `&move` allow mutation without annotations? From 1a37683cf4fc4cd1f121ddc5a05b52e8025aedbe Mon Sep 17 00:00:00 2001 From: John Hodge Date: Fri, 13 May 2016 14:23:31 +0800 Subject: [PATCH 4/6] move_pointer - Drop/DerefMove exclusivity, add example of how code "desugars" --- text/0000-move-pointer.md | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/text/0000-move-pointer.md b/text/0000-move-pointer.md index 93f62869f17..c5a3c49e24e 100644 --- a/text/0000-move-pointer.md +++ b/text/0000-move-pointer.md @@ -23,18 +23,41 @@ This provides an elegant solution to passing DSTs by value, allowing `Box`. With a special case such that `&move |x| x` parses as a move closure, requiring parentheses to parse as an owned pointer to a closure. - This precedence can be implemented by ignoring the `move` when parsing unary operators if it is followed by a `|` or `||` token. - Add a new operator trait `DerefMove` to allow smart pointer types to return owned pointers to their contained data. + - A type that implements `DerefMove` cannot implement `Drop` (as `DerefMove` provides equivalent functinality) ```rust trait DerefMove: DerefMut { /// Return an owned pointer to inner data fn deref_move(&mut self) -> &move Self::Target; - /// Drop self without calling destructor for Self::Target - fn deallocate(self); + /// Equivalent to `Drop::drop` except that the destructor for `Self::Target` is not called + fn deallocate(&mut self); +} +``` + +When an owned pointer is dropped (without having been moved out of), the destructor for the contained data is called (unlike `&mut` pointers, which are just borrows). The backing memory for this pointer is not freed until a point after the `&move` is dropped (likely either at the end of the statement, or at the end of the owning block). + +For example, the following code moves out of a `Box` into a `&move T` and passes it to a function +```rust +fn takes_move(val: &move SomeStruct) { + // ... +} +fn main() { + let val = Box::new( SomeStruct::new() ); + takes_move( &move val ); + println!("Hello"); +} +``` +This becomes the following operations +```rust +fn main() { + let val = Box::new( SomeStruct::new() ); + takes_move( DerefMove::deref_move(&mut val) ); + DerefMove::deallocate(&mut val); + println!("Hello"); } ``` -When an owned pointer is created to a variable (e.g. on the stack) the owner of the pointer takes responsability of calling the destructor on the pointed-to data (and can do any operation assuming that it has full ownership of the object). The original owner still controls the memory allocation used to hold the type, and will deallocate that memory in the same way as if the object had been passed by value. # Drawbacks [drawbacks]: #drawbacks @@ -45,7 +68,8 @@ When an owned pointer is created to a variable (e.g. on the stack) the owner of [alternatives]: #alternatives - Previous discussions have used `&own` as the pointer type - - Since `own` is not a reserved word, such a change would be breaking. + - This name is far closer to the actual nature of the pointer. + - But, since `own` is not a reserved word, such a change would be breaking. # Unresolved questions [unresolved]: #unresolved-questions From 44ab79fb36cd3fa3ff5bf881945a7daac1296f6c Mon Sep 17 00:00:00 2001 From: John Hodge Date: Fri, 13 May 2016 16:32:46 +0800 Subject: [PATCH 5/6] move_pointer - Add example, and flesh out unanswered questions --- text/0000-move-pointer.md | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/text/0000-move-pointer.md b/text/0000-move-pointer.md index c5a3c49e24e..bc35acd4cf6 100644 --- a/text/0000-move-pointer.md +++ b/text/0000-move-pointer.md @@ -20,6 +20,7 @@ This provides an elegant solution to passing DSTs by value, allowing `Box`. With a special case such that `&move |x| x` parses as a move closure, requiring parentheses to parse as an owned pointer to a closure. - This precedence can be implemented by ignoring the `move` when parsing unary operators if it is followed by a `|` or `||` token. - Add a new operator trait `DerefMove` to allow smart pointer types to return owned pointers to their contained data. @@ -76,6 +77,25 @@ fn main() { - `IndexMove` trait to handle moving out of collection types in a similar way to `DerefMove` - Should (can?) the `box` destructuring pattern be implemented using `DerefMove`? -- Potential interactions of what happens when a `&move` is stored -- Should `&move` allow mutation without annotations? +- Potential interactions of what happens when a `&move` is stored. + - If a `&move` is stored in the current scope, when is the original storage freed? + - If a `&move` isn't stored, is the storage freed right then, or when it would have otherwise gone out of scope? + + +# Appendix: Implementations of `DerefMove` +```rust +impl DerefMove for Box +{ + fn deref_move(&mut self) -> &move Self::Target { + unsafe { + &move *(self.0) + } + } + fn deallocate(&mut self) { + unsafe { + heap::deallocate(self.0, mem::size_of_val(&*self.0), mem::align_of_val(&*self.0)); + } + } +} +``` From c914f40e09a71548c77fb942f28dc8fc35eda499 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Mon, 16 May 2016 12:00:50 +0800 Subject: [PATCH 6/6] move_pointer - Cleanup of the unresolved issues section --- text/0000-move-pointer.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/text/0000-move-pointer.md b/text/0000-move-pointer.md index bc35acd4cf6..278f9210b6f 100644 --- a/text/0000-move-pointer.md +++ b/text/0000-move-pointer.md @@ -75,12 +75,14 @@ fn main() { # Unresolved questions [unresolved]: #unresolved-questions -- `IndexMove` trait to handle moving out of collection types in a similar way to `DerefMove` -- Should (can?) the `box` destructuring pattern be implemented using `DerefMove`? - Potential interactions of what happens when a `&move` is stored. - If a `&move` is stored in the current scope, when is the original storage freed? - If a `&move` isn't stored, is the storage freed right then, or when it would have otherwise gone out of scope? +- `IndexMove` trait to handle moving out of collection types in a similar way to `DerefMove` +- Should (can?) the `box` destructuring pattern be implemented using `DerefMove`? + - This would be a larger RFC specifying the `box` pattern using the `Deref` family of traits + # Appendix: Implementations of `DerefMove` ```rust