ownership system in rust

56
郭軒 [:kuoe0] Mozilla [email protected] Ownership System in Rust 2016/07/12 @摩茲寮

Upload: chih-hsuan-kuo

Post on 13-Apr-2017

477 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: Ownership System in Rust

郭⾄至軒 [:kuoe0] Mozilla [email protected]

Ownership System in Rust

2016/07/12 @摩茲⼯工寮

Page 3: Ownership System in Rust

Variable & Memory

A variable name is only a name.

It’s possible that a variable name can not access any memory.

When a variable is declared, Rust allocates memory in stack and heap (if need) for it.

When the owner of resources is destroyed, ALL resources it owned would be released.

Page 4: Ownership System in Rust

Variable & Memory

Stack

Heap

fn main() { let v = vec![1, 2, 3]; }

dynamic memory

static memory

Page 5: Ownership System in Rust

Variable & Memory

Stack

Heap

fn main() { let v = vec![1, 2, 3]; }

dynamic memory

static memory

When the variable is destroyed…

Page 6: Ownership System in Rust

fn main() { let v = vec![1, 2, 3]; }

Variable & Memory

Stack

Heapdynamic memory

static memory

All related resources will be destroyed, too!

Page 7: Ownership System in Rust

Move By Default

Assignment operator is move semantics by default.

There is exactly one variable binding to any resource.

Avoid data racing to guarantee data consistency.

Page 8: Ownership System in Rust

Move By Default

struct Point { x: i32, y: i32 }

fn main() { let v1 = Point{ x: 10, y: 20}; let v2 = v1; println!("{}", v1.x); }

error: use of moved value: `v1.x` [--explain E0382] --> <anon>:9:20 8 |> let v2 = v1; |> -- value moved here 9 |> println!("{}", v1.x); |> ^^^^ value used here after move <std macros>:2:27: 2:58: note: in this expansion of format_args! <std macros>:3:1: 3:54: note: in this expansion of print! (defined in <std macros>) <anon>:9:5: 9:26: note: in this expansion of println! (defined in <std macros>) note: move occurs because `v1` has type `Point`, which does not implement the `Copy` trait

error: aborting due to previous error

compile

Page 9: Ownership System in Rust

Move By Default

struct Point { x: i32, y: i32 }

fn main() { let v1 = Point{ x: 10, y: 20}; let v2 = v1; println!("{}", v1.x); }

error: use of moved value: `v1.x` [--explain E0382] --> <anon>:9:20 8 |> let v2 = v1; |> -- value moved here 9 |> println!("{}", v1.x); |> ^^^^ value used here after move <std macros>:2:27: 2:58: note: in this expansion of format_args! <std macros>:3:1: 3:54: note: in this expansion of print! (defined in <std macros>) <anon>:9:5: 9:26: note: in this expansion of println! (defined in <std macros>) note: move occurs because `v1` has type `Point`, which does not implement the `Copy` trait

error: aborting due to previous error

compile

Use of moved value!

v1.x

Page 10: Ownership System in Rust

Move By Default

stack

struct Point { x: i32, y: i32 }

fn main() { let v1 = Point{ x: 10, y: 20}; let v2 = v1; println!("{}", v1.x); }

Point { x = 10, y = 20 }

names

v1

Page 11: Ownership System in Rust

Move By Default

stack

struct Point { x: i32, y: i32 }

fn main() { let v1 = Point{ x: 10, y: 20}; let v2 = v1; println!("{}", v1.x); }

Point { x = 10, y = 20 }

names

v1

v2

Page 12: Ownership System in Rust

Copyable Type

The types which implement Copy trait can make assignment operator be copy semantics.

Allow to use the variable which be copied.

All primitive types implement the Copy trait.

Page 13: Ownership System in Rust

Copyable Type

fn main() { let v1 = 10; let v2 = v1; println!("v1 = {}", v1); println!("v2 = {}", v2); }

v1 = 10 v2 = 10 Program ended.

run

Page 14: Ownership System in Rust

Copyable Type

fn main() { let v1 = 10; let v2 = v1; println!("v1 = {}", v1); println!("v2 = {}", v2); }

stacknames

v1 i32 { 10 }

Page 15: Ownership System in Rust

Copyable Type

fn main() { let v1 = 10; let v2 = v1; println!("v1 = {}", v1); println!("v2 = {}", v2); }

stacknames

v1 i32 { 10 }

v2 i32 { 10 }

Page 16: Ownership System in Rust

Parameter Passing

Passing parameters is also move semantics by default (no Copy trait).

Developers should return the ownership of parameters by themselves.

Yes, you should return ten variables back if you pass ten parameters into a function. 😜

Page 17: Ownership System in Rust

Parameter Passing

struct Pt { x: i32, y: i32 }

fn dist(v: Pt) -> Pt { println!("{}", v.x * v.x + v.y * v.y); v }

fn main() { let v = Pt{ x: 3, y: 4 }; let v = dist(v); println!("{} {}", v.x, v.y); }

struct Pt { x: i32, y: i32 }

fn dot(v1: Pt, v2: Pt) -> (Pt, Pt) { println!("{}", v1.x * v2.x + v1.y * v2.y); (v1, v2) }

fn main() { let v1 = Pt{ x: 3, y: 4 }; let v2 = Pt{ x: 1, y: 2 }; let (v1, v2) = dot(v1, v2); println!("{} {}", v1.x, v1.y); println!("{} {}", v2.x, v2.y); }

one parameter two parameters

Page 18: Ownership System in Rust

Parameter Passing

struct Pt { x: i32 }

fn square(v: Pt) { println!("{}", v.x * v.x); }

fn main() { let v = Pt{ x: 3 }; square(v); println!("{}", v.x); }

error: use of moved value: `v.x` [--explain E0382] --> <anon>:10:20 9 |> square(v); |> - value moved here 10 |> println!("{}", v.x); |> ^^^ value used here after move <std macros>:2:27: 2:58: note: in this expansion of format_args! <std macros>:3:1: 3:54: note: in this expansion of print! (defined in <std macros>) <anon>:10:5: 10:25: note: in this expansion of println! (defined in <std macros>) note: move occurs because `v` has type `Pt`, which does not implement the `Copy` trait

error: aborting due to previous error

compile

Page 19: Ownership System in Rust

Parameter Passing

struct Pt { x: i32 }

fn square(v: Pt) { println!("{}", v.x * v.x); }

fn main() { let v = Pt{ x: 3 }; square(v); println!("{}", v.x); }

error: use of moved value: `v.x` [--explain E0382] --> <anon>:10:20 9 |> square(v); |> - value moved here 10 |> println!("{}", v.x); |> ^^^ value used here after move <std macros>:2:27: 2:58: note: in this expansion of format_args! <std macros>:3:1: 3:54: note: in this expansion of print! (defined in <std macros>) <anon>:10:5: 10:25: note: in this expansion of println! (defined in <std macros>) note: move occurs because `v` has type `Pt`, which does not implement the `Copy` trait

error: aborting due to previous error

compile

v.x

Use of moved value!

Page 20: Ownership System in Rust

4.9 Reference and Borrowing[link]

Page 21: Ownership System in Rust

Syntax of Reference

fn main() { let a = 1; let b = &a; // &a is the reference to a let mut c = 2; let d = &mut c; // &mut c is the mutable reference to c }

Page 22: Ownership System in Rust

Borrowing

Use the references to borrow the ownership.

The ownership will return to original owner when the borrower is destroyed automatically.

References are immutable.

Allow multiple references to one variable.

A borrowed variable can be read but not written.

Only allow to borrow the variable with longer lifetime.

Page 23: Ownership System in Rust

Borrowing

fn main() { let orig = 0; let b1 = &orig; let b2 = &orig; let b3 = &orig; println!("b1 = {}", b1); println!("b2 = {}", b2); println!("b3 = {}", b3); println!("orig = {}", orig); }

b1 = 0 b2 = 0 b3 = 0 orig = 0 Program ended.

run

Page 24: Ownership System in Rust

Borrowing

fn main() { let mut x = 0; { let y = &x; x += 1; println!("{}", y); } println!("{}", x); }

error: cannot assign to `x` because it is borrowed [--explain E0506] --> <anon>:5:9 4 |> let y = &x; |> - borrow of `x` occurs here 5 |> x += 1; |> ^^^^^^ assignment to borrowed `x` occurs here

error: aborting due to previous errorcompile

Page 25: Ownership System in Rust

Borrowing

fn main() { let mut x = 0; { let y = &x; x += 1; println!("{}", y); } println!("{}", x); }

error: cannot assign to `x` because it is borrowed [--explain E0506] --> <anon>:5:9 4 |> let y = &x; |> - borrow of `x` occurs here 5 |> x += 1; |> ^^^^^^ assignment to borrowed `x` occurs here

error: aborting due to previous errorcompile

x += 1;

Cannot write the borrowed variable!

Page 26: Ownership System in Rust

Borrowing

fn main() { let y: &i32; { let x = 5; y = &x; } println!("{}", y); }

error: `x` does not live long enough --> <anon>:5:14 5 |> y = &x; |> ^ note: reference must be valid for the block suffix following statement 0 at 2:16... --> <anon>:2:17 2 |> let y: &i32; |> ^ note: ...but borrowed value is only valid for the block suffix following statement 0 at 4:18 --> <anon>:4:19 4 |> let x = 5; |> ^

error: aborting due to previous error

compile

Page 27: Ownership System in Rust

Borrowing

fn main() { let y: &i32; { let x = 5; y = &x; } println!("{}", y); }

error: `x` does not live long enough --> <anon>:5:14 5 |> y = &x; |> ^ note: reference must be valid for the block suffix following statement 0 at 2:16... --> <anon>:2:17 2 |> let y: &i32; |> ^ note: ...but borrowed value is only valid for the block suffix following statement 0 at 4:18 --> <anon>:4:19 4 |> let x = 5; |> ^

error: aborting due to previous error

compile

y = &x;

Lifetime of x is shorter than y.

Page 28: Ownership System in Rust

Borrowing

fn main() { let y: &i32; let x = 5; y = &x; println!("{}", y); }

error: `x` does not live long enough --> <anon>:4:10 4 |> y = &x; |> ^ note: reference must be valid for the block suffix following statement 0 at 2:16... --> <anon>:2:17 2 |> let y: &i32; |> ^ note: ...but borrowed value is only valid for the block suffix following statement 1 at 3:14 --> <anon>:3:15 3 |> let x = 5; |> ^

error: aborting due to previous error

compile

Page 29: Ownership System in Rust

Borrowing

fn main() { let y: &i32; let x = 5; y = &x; println!("{}", y); }

error: `x` does not live long enough --> <anon>:4:10 4 |> y = &x; |> ^ note: reference must be valid for the block suffix following statement 0 at 2:16... --> <anon>:2:17 2 |> let y: &i32; |> ^ note: ...but borrowed value is only valid for the block suffix following statement 1 at 3:14 --> <anon>:3:15 3 |> let x = 5; |> ^

error: aborting due to previous error

compile

y = &x;

Lifetime of x is shorter than y.

Page 30: Ownership System in Rust

Borrowing

struct Pt { x: i32, y: i32 }

fn dot(v1: Pt, v2: Pt) -> (Pt, Pt) { println!("{}", v1.x * v2.x + v1.y * v2.y); (v1, v2) }

fn main() { let v1 = Pt{ x: 3, y: 4 }; let v2 = Pt{ x: 1, y: 2 }; let (v1, v2) = dot(v1, v2); println!("{} {}", v1.x, v1.y); println!("{} {}", v2.x, v2.y); }

struct Pt { x: i32, y: i32 }

fn dot(v1: &Pt, v2: &Pt) { println!("{}", v1.x * v2.x + v1.y * v2.y); }

fn main() { let v1 = Pt{ x: 3, y: 4 }; let v2 = Pt{ x: 1, y: 2 }; dot(&v1, &v2); println!("{} {}", v1.x, v1.y); println!("{} {}", v2.x, v2.y); }

Page 31: Ownership System in Rust

Borrowing

struct Pt { x: i32, y: i32 }

fn dot(v1: Pt, v2: Pt) -> (Pt, Pt) { println!("{}", v1.x * v2.x + v1.y * v2.y); (v1, v2) }

fn main() { let v1 = Pt{ x: 3, y: 4 }; let v2 = Pt{ x: 1, y: 2 }; let (v1, v2) = dot(v1, v2); println!("{} {}", v1.x, v1.y); println!("{} {}", v2.x, v2.y); }

struct Pt { x: i32, y: i32 }

fn dot(v1: &Pt, v2: &Pt) { println!("{}", v1.x * v2.x + v1.y * v2.y); }

fn main() { let v1 = Pt{ x: 3, y: 4 }; let v2 = Pt{ x: 1, y: 2 }; dot(&v1, &v2); println!("{} {}", v1.x, v1.y); println!("{} {}", v2.x, v2.y); }

Page 32: Ownership System in Rust

Mutable Borrowing

Use mutable references only if you need to change the values you borrowed.

Only allow to borrow a mutable variables as a mutable reference.

There is exactly one mutable reference to a variable.

A variable borrowed as a mutable reference can not be borrowed as immutable references.

A variable borrowed as a mutable reference can not be used until the end of borrowing.

Page 33: Ownership System in Rust

Mutable Borrowing

fn main() { let mut x = 0; { let y = &mut x; *y += 1; } println!("x = {}", x); }

x = 1 Program ended.

run

Page 34: Ownership System in Rust

Mutable Borrowing

fn main() { let mut x = 0; { let y = &mut x; let z = &mut x; *y += 1; } println!("x = {}", x); }

error: cannot borrow `x` as mutable more than once at a time [--explain E0499] --> <anon>:5:22 4 |> let y = &mut x; |> - first mutable borrow occurs here 5 |> let z = &mut x; |> ^ second mutable borrow occurs here 6 |> *y += 1; 7 |> } |> - first borrow ends here

error: aborting due to previous error

compile

Page 35: Ownership System in Rust

Mutable Borrowing

fn main() { let mut x = 0; { let y = &mut x; let z = &mut x; *y += 1; } println!("x = {}", x); }

error: cannot borrow `x` as mutable more than once at a time [--explain E0499] --> <anon>:5:22 4 |> let y = &mut x; |> - first mutable borrow occurs here 5 |> let z = &mut x; |> ^ second mutable borrow occurs here 6 |> *y += 1; 7 |> } |> - first borrow ends here

error: aborting due to previous error

compile

let z = &mut x;

Cannot borrow x as mutable reference more than once!

Page 36: Ownership System in Rust

Mutable Borrowing

fn main() { let mut x = 0; { let y = &mut x; let z = &x; *y += 1; } println!("x = {}", x); }

error: cannot borrow `x` as immutable because it is also borrowed as mutable [--explain E0502] --> <anon>:6:18 4 |> let y = &mut x; |> - mutable borrow occurs here 5 |> *y += 1; 6 |> let z = &x; |> ^ immutable borrow occurs here 7 |> } |> - mutable borrow ends here

error: aborting due to previous error

compile

Page 37: Ownership System in Rust

Mutable Borrowing

fn main() { let mut x = 0; { let y = &mut x; let z = &x; *y += 1; } println!("x = {}", x); }

error: cannot borrow `x` as immutable because it is also borrowed as mutable [--explain E0502] --> <anon>:6:18 4 |> let y = &mut x; |> - mutable borrow occurs here 5 |> *y += 1; 6 |> let z = &x; |> ^ immutable borrow occurs here 7 |> } |> - mutable borrow ends here

error: aborting due to previous error

compile

let z = &x;

Cannot borrow the variable been borrowed as a mutable reference!

Page 38: Ownership System in Rust

Mutable Borrowing

fn main() { let mut x = 0; { let y = &mut x; let z = x + 1; } println!("x = {}", x); }

error: cannot use `x` because it was mutably borrowed [E0503] --> <anon>:5:17 5 |> let z = x + 1; |> ^ note: borrow of `x` occurs here --> <anon>:4:22 4 |> let y = &mut x; |> ^

error: aborting due to previous errorcompile

Page 39: Ownership System in Rust

Mutable Borrowing

fn main() { let mut x = 0; { let y = &mut x; let z = x + 1; } println!("x = {}", x); }

error: cannot use `x` because it was mutably borrowed [E0503] --> <anon>:5:17 5 |> let z = x + 1; |> ^ note: borrow of `x` occurs here --> <anon>:4:22 4 |> let y = &mut x; |> ^

error: aborting due to previous errorcompile

let z = x + 1;

Cannot access the variable been borrowed as a mutable reference.

Page 40: Ownership System in Rust

Thinking in Scopes

fn main() { let mut x = 0; let y = &mut x; *y += 1; println!("x = {}", x); }

Why compile error?

Page 41: Ownership System in Rust

Thinking in Scopes (cont’)

fn main() { let mut x = 0; let y = &mut x; *y += 1; println!("x = {}", x); }

error: cannot borrow `x` as immutable because it is also borrowed as mutable [--explain E0502] --> <anon>:5:24 3 |> let y = &mut x; |> - mutable borrow occurs here 4 |> *y += 1; 5 |> println!("x = {}", x); |> ^ immutable borrow occurs here 6 |> } |> - mutable borrow ends here <std macros>:2:27: 2:58: note: in this expansion of format_args! <std macros>:3:1: 3:54: note: in this expansion of print! (defined in <std macros>) <anon>:5:5: 5:27: note: in this expansion of println! (defined in <std macros>)

error: aborting due to previous error

compile

Page 42: Ownership System in Rust

Thinking in Scopes (cont’)

fn main() { let mut x = 0; let y = &mut x; *y += 1; println!("x = {}", x); }

error: cannot borrow `x` as immutable because it is also borrowed as mutable [--explain E0502] --> <anon>:5:24 3 |> let y = &mut x; |> - mutable borrow occurs here 4 |> *y += 1; 5 |> println!("x = {}", x); |> ^ immutable borrow occurs here 6 |> } |> - mutable borrow ends here <std macros>:2:27: 2:58: note: in this expansion of format_args! <std macros>:3:1: 3:54: note: in this expansion of print! (defined in <std macros>) <anon>:5:5: 5:27: note: in this expansion of println! (defined in <std macros>)

error: aborting due to previous error

compile

println!("x = {}", x);

Immutable borrow occurs here!

Page 43: Ownership System in Rust

Iterator Invalidation

fn main() { let mut v = vec![1, 2, 3];

for i in &v { println!("{}", i); v.push(34); } }

error: cannot borrow `v` as mutable because it is also borrowed as immutable [--explain E0502] --> <anon>:6:9 4 |> for i in &v { |> - immutable borrow occurs here 5 |> println!("{}", i); 6 |> v.push(34); |> ^ mutable borrow occurs here 7 |> } |> - immutable borrow ends here

error: aborting due to previous error

compile

Page 44: Ownership System in Rust

Iterator Invalidation

fn main() { let mut v = vec![1, 2, 3];

for i in &v { println!("{}", i); v.push(34); } }

error: cannot borrow `v` as mutable because it is also borrowed as immutable [--explain E0502] --> <anon>:6:9 4 |> for i in &v { |> - immutable borrow occurs here 5 |> println!("{}", i); 6 |> v.push(34); |> ^ mutable borrow occurs here 7 |> } |> - immutable borrow ends here

error: aborting due to previous error

compilev.push(34);

push(&mut self, …) try to borrow v as a mutable reference!

Page 46: Ownership System in Rust

Syntax of Lifetimes Specifier

fn fn2<'a>(x: &'a i32) -> &'a i32 { // do something }

Page 47: Ownership System in Rust

Syntax of Lifetimes Specifier

fn fn2<'a>(x: &'a i32) -> &'a i32 { // do something }

'a 'a 'a

input lifetime output lifetime

Page 48: Ownership System in Rust

Syntax of Lifetimes Specifier

// lifetime with mutable reference fn foo<'a>(x: &'a mut i32) -> &'a mut i32 { // do something }

// lifetime in struct struct Foo<'a> { x: &'a i32 }

// lifetime with impl impl<'a> Foo<'a> { fn x(&self) -> &'a i32 { self.x } }

Page 49: Ownership System in Rust

Lifetimes Specifier

All references need lifetimes.

Explicit lifetimes are used to make lifetime inference unambiguous.

Must give explicit lifetimes for struct contain reference members.

No need to give explicit lifetimes to the functions without references return.

Page 50: Ownership System in Rust

Lifetimes Specifier

struct Foo<'a> { x: &'a i32 }

impl<'a> Foo<'a> { fn x(&self) -> &'a i32 { self.x } }

fn main() { let y = 5; let f = Foo { x: &y };

println!("{}", f.x); println!("{}", f.x()); }

5 5 Program ended.

run

Page 51: Ownership System in Rust

Lifetime Inference

Each elided lifetime of arguments becomes a distinct lifetime parameter.

If there is exactly one input lifetime, all elided lifetimes of the return values will be as same as the input lifetime.

If there is &self in input lifetimes, all elided lifetimes of the return values will be as same as &self.

Page 52: Ownership System in Rust

Lifetime Inference (valid)

fn print(s: &str); // elided fn print<'a>(s: &'a str); // expanded

//////////

fn substr(s: &str, until: u32) -> &str; // elided fn substr<'a>(s: &'a str, until: u32) -> &'a str; // expanded

//////////

fn get_mut(&mut self) -> &mut T; // elided fn get_mut<'a>(&'a mut self) -> &'a mut T; // expanded

Page 53: Ownership System in Rust

Lifetime Inference (invalid)

fn get_str() -> &str; // ILLEGAL, no inputs

//////////

fn frob(s: &str, t: &str) -> &str; // Two input lifetimes fn frob<'a, 'b>(s: &'a str, t: &'b str) -> &str; // Output lifetime is ambiguous

Page 54: Ownership System in Rust

Static Lifetime

Some resources have the lifetime of the entire program.

No need to give explicit lifetime for functions return static resources.

Page 55: Ownership System in Rust

Lifetimes Specifier

static FIVE: i32 = 5;

fn get_five() -> &'static i32 { &FIVE }

fn main() { let x = get_five(); println!("x = {}", x); }

x = 5 Program ended.

run