Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -0,0 +1,217 @@
|
||||
# Ownership
|
||||
|
||||
V has an optional ownership system inspired by Rust that tracks owned values and prevents
|
||||
use-after-move bugs at compile time. It is currently focused on strings and is enabled
|
||||
with the `-ownership` flag.
|
||||
|
||||
## Quick start
|
||||
|
||||
```v okfmt
|
||||
fn main() {
|
||||
s1 := 'hello'.to_owned()
|
||||
s2 := s1 // s1 is moved to s2
|
||||
println(s1) // error: use of moved value: `s1`
|
||||
}
|
||||
```
|
||||
|
||||
Compile with ownership checking:
|
||||
|
||||
```
|
||||
v2 -ownership -o out main.v
|
||||
```
|
||||
|
||||
## Creating owned values
|
||||
|
||||
Call `.to_owned()` on a string to create an owned copy. Only strings created with
|
||||
`.to_owned()` participate in ownership tracking — regular string literals and primitive
|
||||
types (int, f64, bool, ...) are unaffected.
|
||||
|
||||
```v okfmt
|
||||
s := 'hello'.to_owned() // s is owned
|
||||
t := 'world' // t is a normal string, no ownership tracking
|
||||
```
|
||||
|
||||
## Move semantics
|
||||
|
||||
Assigning an owned value to another variable **moves** it. The original variable
|
||||
becomes unusable:
|
||||
|
||||
```v okfmt
|
||||
s1 := 'hello'.to_owned()
|
||||
s2 := s1 // move
|
||||
println(s1) // error: use of moved value: `s1`
|
||||
```
|
||||
|
||||
Passing an owned value to a function also moves it:
|
||||
|
||||
```v okfmt
|
||||
fn takes_ownership(s string) {
|
||||
println(s)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
s := 'hello'.to_owned()
|
||||
takes_ownership(s)
|
||||
println(s) // error: use of moved value: `s`
|
||||
}
|
||||
```
|
||||
|
||||
### Preventing moves with `.clone()`
|
||||
|
||||
Use `.clone()` to make an independent copy instead of moving:
|
||||
|
||||
```v okfmt
|
||||
s1 := 'hello'.to_owned()
|
||||
s2 := s1.clone() // s1 is NOT moved
|
||||
println(s1) // ok
|
||||
println(s2) // ok
|
||||
```
|
||||
|
||||
```v okfmt
|
||||
takes_ownership(s.clone()) // s is NOT moved
|
||||
println(s) // ok
|
||||
```
|
||||
|
||||
## Borrowing
|
||||
|
||||
Pass `&variable` to borrow without moving. The original stays usable:
|
||||
|
||||
```v okfmt
|
||||
fn calculate_length(s &string) int {
|
||||
return s.len
|
||||
}
|
||||
|
||||
fn main() {
|
||||
s := 'hello'.to_owned()
|
||||
len := calculate_length(&s)
|
||||
println(s) // ok — s was borrowed, not moved
|
||||
}
|
||||
```
|
||||
|
||||
### Explicit lifetimes
|
||||
|
||||
Ownership mode also supports explicit named lifetimes with `^name`.
|
||||
|
||||
Use `&^a T` for a borrowed reference with an explicit lifetime and `[^a]`
|
||||
in generic parameter and argument lists:
|
||||
|
||||
```v ignore
|
||||
struct Ignore {}
|
||||
|
||||
struct IgnoreMatch[^a] {}
|
||||
|
||||
fn matched_dir_entry[^a](self &^a Ignore) IgnoreMatch[^a] {
|
||||
return IgnoreMatch[^a]{}
|
||||
}
|
||||
```
|
||||
|
||||
`^` is used instead of Rust's `'` because `'` is already used for string and
|
||||
character literals in V.
|
||||
|
||||
Multiple immutable borrows are allowed:
|
||||
|
||||
```v okfmt
|
||||
s := 'hello'.to_owned()
|
||||
r1 := &s
|
||||
r2 := &s // ok
|
||||
```
|
||||
|
||||
Mutable borrows via `mut` parameters work too — the variable is usable after the
|
||||
call returns:
|
||||
|
||||
```v ignore
|
||||
fn append_world(mut s string) {
|
||||
s = s + ' world'
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut s := 'hello'.to_owned()
|
||||
append_world(mut s)
|
||||
println(s) // ok — prints "hello world"
|
||||
}
|
||||
```
|
||||
|
||||
### Borrow restrictions
|
||||
|
||||
A borrowed variable cannot be moved or reassigned while the borrow is active:
|
||||
|
||||
```v okfmt
|
||||
s := 'hello'.to_owned()
|
||||
r := &s
|
||||
s2 := s // error: cannot move `s` because it is borrowed
|
||||
```
|
||||
|
||||
```v okfmt
|
||||
mut s := 'hello'.to_owned()
|
||||
r := &s
|
||||
s = 'world'.to_owned() // error: cannot assign to `s` because it is borrowed
|
||||
```
|
||||
|
||||
## Return value ownership
|
||||
|
||||
Functions that create and return owned values transfer ownership to the caller:
|
||||
|
||||
```v okfmt
|
||||
fn gives_ownership() string {
|
||||
return 'hello'.to_owned()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
s1 := gives_ownership() // s1 is owned
|
||||
s2 := s1 // move
|
||||
println(s1) // error: use of moved value
|
||||
}
|
||||
```
|
||||
|
||||
Functions that return a parameter pass ownership through:
|
||||
|
||||
```v okfmt
|
||||
fn takes_and_gives_back(s string) string {
|
||||
return s
|
||||
}
|
||||
|
||||
fn main() {
|
||||
s1 := 'hello'.to_owned()
|
||||
s2 := takes_and_gives_back(s1) // s1 moved in, ownership comes back as s2
|
||||
println(s1) // error: s1 was moved
|
||||
println(s2) // ok
|
||||
}
|
||||
```
|
||||
|
||||
## Full example
|
||||
|
||||
From the Rust book, translated to V:
|
||||
|
||||
```v okfmt
|
||||
fn gives_ownership() string {
|
||||
s := 'hello'.to_owned()
|
||||
return s
|
||||
}
|
||||
|
||||
fn takes_and_gives_back(a_string string) string {
|
||||
return a_string
|
||||
}
|
||||
|
||||
fn main() {
|
||||
s1 := gives_ownership()
|
||||
s2 := 'hello'.to_owned()
|
||||
s3 := takes_and_gives_back(s2)
|
||||
println(s1) // ok
|
||||
println(s3) // ok
|
||||
}
|
||||
```
|
||||
|
||||
## Enabling ownership checking
|
||||
|
||||
Ownership checking is compiled into a separate `v2_ownership` binary using V's
|
||||
compile-time defines so there is zero overhead in the normal `v2` binary.
|
||||
|
||||
```
|
||||
v2 -ownership file.v # check and compile
|
||||
```
|
||||
|
||||
To build the ownership-enabled compiler manually:
|
||||
|
||||
```
|
||||
v -d ownership -o v2_ownership cmd/v2/v2.v
|
||||
```
|
||||
Reference in new issue
Block a user