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: src/ch04-01-what-is-ownership.md
+11-18Lines changed: 11 additions & 18 deletions
Original file line number
Diff line number
Diff line change
@@ -1,10 +1,6 @@
1
1
## Ownership Using a Linear Type System
2
2
3
-
Cairo uses a linear type system for two main reasons:
4
-
- Ensuring that all code is provable and thus verifiable.
5
-
- Abstracting away the immutable memory of the Cairo VM.
6
-
7
-
In a linear type system, any value (a basic type, a struct, an enum) must be used and must only be used once. 'Used' here means that the value is either _destroyed_ or _moved_.
3
+
Cairo uses a linear type system. In such a type system, any value (a basic type, a struct, an enum) must be used and must only be used once. 'Used' here means that the value is either _destroyed_ or _moved_.
8
4
9
5
_Destruction_ can happen in several ways:
10
6
- a variable goes out of scope
@@ -15,10 +11,13 @@ _Moving_ a value simply means passing that value to another function.
15
11
16
12
This results in somewhat similar constraints to the Rust ownership model, but there are some differences.
17
13
In particular, the rust ownership model exists (in part) to avoid data races and concurrent mutable access to a memory value. This is obviously impossible in Cairo since the memory is immutable.
14
+
Instead, Cairo leverages its linear type system for two main purposes:
15
+
- Ensuring that all code is provable and thus verifiable.
16
+
- Abstracting away the immutable memory of the Cairo VM.
18
17
19
18
#### Ownership
20
19
21
-
In Cairo, ownership applies to _variables_ and not to _values_. A value can safely be pointed to by many different variables (even if they are mutable variables), as the value itself is always immutable.
20
+
In Cairo, ownership applies to _variables_ and not to _values_. A value can safely be referred to by many different variables (even if they are mutable variables), as the value itself is always immutable.
22
21
Variables however can be mutable, so the compiler must ensure that constant variables aren't accidentally modified by the programmer.
23
22
This makes it possible to talk about ownership of a variable: the owner is the code that can read (and write if mutable) the variable.
24
23
@@ -74,13 +73,12 @@ Here is a short reminder of what an array looks like:
74
73
How does the type system ensure that the Cairo program never tries to write to the same memory cell twice?
75
74
Consider the following code, where we try to remove the front of the array twice:
In this case, we try to pass the same value (the array in `arr`) to both function calls. This means our code tries to remove the first element twice, which would try to write to the same memory cell twice - which is forbidden by the Cairo VM, leading to a runtime error.
83
-
Thankfully, this code does not actually compile. Once we have passed the array, the variable is no longer usable. We get this compile-time error, telling us that we would need Array to implement the Copy Trait:
80
+
In this case, we try to pass the same value (the array in the `arr` variable) to both function calls. This means our code tries to remove the first element twice, which would try to write to the same memory cell twice - which is forbidden by the Cairo VM, leading to a runtime error.
81
+
Thankfully, this code does not actually compile. Once we have passed the array to the `foo` function, the variable`arr` is no longer usable. We get this compile-time error, telling us that we would need Array to implement the Copy Trait:
84
82
85
83
```shell
86
84
error: Variable was previously moved. Trait has no implementation in context: core::traits::Copy::<core::array::Array::<core::integer::u128>>
@@ -101,15 +99,15 @@ While Arrays and Dictionaries can't be copied, custom types that don't contain e
In this example, we can pass `p1` twice to the foo function because the `Point` type implements the `Copy` trait. This means that when we pass `p1` to `foo`, we are actually passing a copy of `p1`, so `p1` remains valid.
102
+
In this example, we can pass `p1` twice to the foo function because the `Point` type implements the `Copy` trait. This means that when we pass `p1` to `foo`, we are actually passing a copy of `p1`, so `p1` remains valid. In ownership terms, this means that the ownership of `p1` remains with the main function.
105
103
If you remove the `Copy` trait derivation from the `Point` type, you will get a compile-time error when trying to compile the code.
106
104
107
105
_Don't worry about the `Struct` keyword. We will introduce this in [Chapter 5](ch05-00-using-structs-to-structure-related-data.md)._
108
106
109
107
### Destroying values - example with FeltDict
110
108
111
109
The other way linear types can be _used_ is by being destroyed. Destruction must ensure that the 'resource' is now correctly released. In rust for example, this could be closing the access to a file, or locking a mutex.
112
-
In Cairo, one type that has such behaviour is felt252Dict. For provability, dicts must be 'squashed' when they are destructed.
110
+
In Cairo, one type that has such behaviour is `Felt252Dict`. For provability, dicts must be 'squashed' when they are destructed.
113
111
This would be very easy to forget, so it is enforced by the type system and the compiler.
114
112
115
113
#### No-op destruction: the Drop Trait
@@ -130,9 +128,9 @@ For example, the following code compiles:
130
128
```
131
129
132
130
#### Destruction with a side-effect: the Destruct trait
133
-
When a value is destroyed, the compiler calls the `destruct` method on that type. This method is provided by the `Destruct` trait. This is actually how `Drop` is implemented at the moment, by providing a `Destruct`impl that does nothing.
131
+
When a value is destroyed, the compiler first tries to call the `drop` method on that type. If it doesn't exist, then the compiler tries to call `destruct` instead. This method is provided by the `Destruct`trait.
134
132
135
-
As said earlier, dictionaries in Cairo are types that must be "squashed" when destructed, so that the sequence of access can be proven. This is easy for developers to forget, so instead dictionaries implement the Destruct Trait.
133
+
As said earlier, dictionaries in Cairo are types that must be "squashed" when destructed, so that the sequence of access can be proven. This is easy for developers to forget, so instead dictionaries implement the `Destruct` trait to ensure that all dictionaries are _squashed_ when going out of scope.
136
134
As such, the following example will not compile:
137
135
138
136
```rust,does_not_compile
@@ -156,11 +154,6 @@ When A goes out of scope, it can't be dropped as it implements neither the `Drop
156
154
157
155
Now, when `A` goes out of scope, its dictionary will be automatically `squashed`, and the program will compile.
158
156
159
-
#### Copy vs Destruct
160
-
161
-
You may notice that these two traits are somewhat incompatible. With a `Copy` type, we could have two variables pointing to the same value, which would lead to a double-destruction when both variables go out of scope. This is obviously quite broken for any type with a non-trivial `Destruct` implementation.
162
-
For this reason, most types are either `Copy` and `Drop`, just `Drop`, or none. A `Copy` and `Destruct`type is possible, but be aware that this is rather odd, and possibly an indicator of a 'gotcha'.
163
-
164
157
### Copy Array data with Clone
165
158
166
159
If we _do_ want to deeply copy the data of an `Array`, we can use a common method called `clone`. We’ll discuss method syntax in Chapter 6, but because methods are a common feature in many programming languages, you’ve probably seen them before.
0 commit comments