You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardexpand all lines: text/0000-immovable-types.md
+61-25
Original file line number
Diff line number
Diff line change
@@ -20,47 +20,84 @@ Since generators can only move during suspend points we can require that referen
20
20
# Detailed design
21
21
[design]: #detailed-design
22
22
23
-
A new unsafe auto trait `Move` is introduced in `core::marker`. Auto traits are implemented for all primitive types and for composite types where the elements also implement the trait. Users can opt-out to this for custom types. References, pointers, `core::ptr::Unique` and `core::ptr::Shared` explicitly implement this trait, since pointers are movable even if they point to immovable types.
24
-
25
-
All type parameters (including `Self` for traits), trait objects and associated types have a `Move` bound by default.
23
+
A new builtin marker trait `Move` is introduced in `core::marker`. All type parameters (including `Self` for traits) and associated types implement `Move` by default.
26
24
27
25
If you want to allow types which may not implement `Move`, you would use the `?Move` trait bound which means that the type may or may not implement `Move`.
28
26
27
+
A new marker struct `Immovable` is also introduced in `core::marker`. This struct does not implement `Move` and allows users to make composite immovable types.
28
+
29
29
You can freely move values which are known to implement `Move` after they are borrowed, however you cannot move types which aren't known to implement `Move` after they have been borrowed. Once we borrow an immovable type, we'd know its address and code should be able to rely on the address not changing. This is sound since the only way to observe the address of a value is to borrow it. Before the first borrow nothing can observe the address and the value can be moved around.
30
30
31
31
Static variables allow types which do not implement `Move`.
32
32
33
+
These are the rules to determine if a type implements `Move`:
34
+
- Integers, floats, `char` and `bool` are `Move`
35
+
- Function types and function pointers are `Move`
36
+
- Closures are `Move` if their captured variables are `Move`
37
+
- Movable generators are `Move` if all values (including capture variables) which are live during a suspension point are `Move`
38
+
- Imovable generators are never `Move`
39
+
- The `Immovable` type is never `Move`
40
+
- Trait objects are `Move` if they have an explicit `Move` bound
41
+
- Struct, enums and tuples are `Move` if all their elements are `Move`
42
+
- Existential types (`impl Trait`) are `Move` if their underlying type are `Move`
43
+
-`[T]` and `[T; n]` are `Move` if `T` is `Move`
44
+
-`str` is `Move`
45
+
33
46
## Move checking
34
47
35
-
To prevent values which may not implement `Move` that have been previously borrowed we introduce a MIR pass. We do a forward dataflow analysis on the MIR marking l-values that have been borrowed. For `a` we mark the path `a` as observed. For `a.b` we mark both `a.b` and the parent `a`, since if you observe `a.b` moving `a` will change the address of `a.b`. For `*a` we do nothing, as the address of `a` isn't observed. For slice indices `a[i]`, we mark `a` as observed. Finally we walk through every move in the MIR and give an error if the moved value could be observed at that point and the type of the value isn't known to implement `Move`.
48
+
We need to ensure that values we have borrowed no longer can be moved. When we borrow a value we can find its address in memory. For example:
49
+
```rust
50
+
structFoo {
51
+
field:bool,
52
+
}
36
53
37
-
## Immovable types contained in movable types
54
+
fnaddress(v:&Foo) ->usize {
55
+
vas*const_asusize
56
+
}
38
57
39
-
To allow immovable types to be contained in movable types, we introduce a `core::cell::MobileCell` wrapper which itself implements `Move`. It works similarly to `Cell` in that it disallows references to the value inside.
58
+
leta=Foo {
59
+
field:true
60
+
};
61
+
address(&a)
62
+
```
63
+
We can also find the address of `a.field` in our `address function using this code:
40
64
```rust
41
-
#[lang ="mobile_cell"]
42
-
pubstructMobileCell<T:?Move> {
43
-
value:T,
65
+
fnaddress(v:&Foo) ->usize {
66
+
&v.field as*const_asusize
44
67
}
68
+
```
69
+
Thus we say that our borrow `&a` observes both `a` and `a.field`.
70
+
In general, borrowing a value observes all other values stored inside except for values which are reached using an indirection (for example a reference or a `Box`). If any elements of an array is observed, the entire array is also observed.
45
71
46
-
unsafeimpl<T:?Move> MoveforMobileCell<T> {}
72
+
Whenever we are moving an value we emit an error if the type does implement `Move` and the value could have been observed. We also do the same check for any values stored inside; again ignoring indirections.
47
73
48
-
impl<T:?Move> MobileCell<T> {
49
-
pubconstfnnew(value:T) ->Movable<T> {
50
-
Movable {
51
-
value:value,
52
-
}
53
-
}
74
+
## Immovable types contained in movable types
54
75
55
-
pubfninto_inner(self) ->T {
56
-
self.value
57
-
}
76
+
To allow immovable types to be contained in movable types, we introduce a `core::cell::MovableCell` wrapper which itself implements `Move`. It works similarly to `Cell` in that it disallows references to the value inside.
0 commit comments