References and borrowing, lifetimes in Rust
hardcore
17
Rust
27
White

Giang Nguyen viết ngày 11/10/2016

1. References and borrowing

fn take_vec(vec: Vec<i32>) {}
fn main() {
    let vec = vec![1, 2, 3];

    take_vec(vec);
    println!("{:?}", vec);
}

Với đoạn code trên, chúng ta đã move ownership biến vec cho function take_vec, do đó nếu chúng ta muốn dùng lại vec thì không thể được, cụ thể là println ra thì sẽ báo lỗi khi compile:

error[E0382]: use of moved value: `vec`
  --> src/main.rs:82:22
   |
81 |     take_vec(vec);
   |              --- value moved here
82 |     println!("{:?}", vec);
   |                      ^^^ value used here after move

Nếu chúng ta muốn dùng lại vec thì làm thế làm nào ?, clone nó ra !

fn take_vec(vec: Vec<i32>) {}
fn main() {
    let vec = vec![1, 2, 3];
    let vec_cloned = vec.clone();
    take_vec(vec_cloned);
    println!("{:?}", vec);
}

Nhưng đó là không phải là điều mình muốn nói tới, chúng ta có một cách đó là thay vì move ownership cho take_vec thì chúng ta chỉ cho nó mượn thôi, nên chúng ta truyền param cho take_vec kiểu &Vec<i32>

fn take_vec(vec: &Vec<i32>) {}
fn main() {
    let vec = vec![1, 2, 3];
    take_vec(&vec);
    println!("{:?}", vec);
}

&Vec<i32> hay là &Treference, thay vì lấy đi ownership của resources, thì chúng ta chỉ borrowing resources. Việc borrowing resources không deallocate nó khi resources đó ra khỏi scope, nên chúng ta vẫn dùng lại được giá trị original.

Khi refereces 1 resources nào đó, thì chúng ta không thể mutable resources đó


fn foo(v: &Vec<i32>) {
    v.push(5);
}

fn main() {
    let v: Vec<i32> = vec![1, 2, 3];
    foo(&v);
    println!("{:?}", v);
}

Vậy khi compile đoạn code trên , we get errros :smile:

error: cannot borrow immutable borrowed content `*v` as mutable

Nhưng Rust hỗ trợ chúng ta việc mutable resources từ borrowing là &mut T

fn foo(v: &mut Vec<i32>) {
    v.push(5);
}

fn main() {
    let mut v: Vec<i32> = vec![1, 2, 3];
    {
        let y = &mut v;
        foo(y);
    }
    println!("{:?}", v);
}

Compile code, chúng ta có thể thấy vec lúc này sẽ là: [1, 2, 3, 5]

Chúng ta đã mutable resource từ việc borrowing, nhưng tại sao lại có {}, thừ bỏ 2 cái đó đi và compile lại

 fn foo(v: &mut Vec<i32>) {
    v.push(5);
}

fn main() {
    let mut v: Vec<i32> = vec![1, 2, 3];
        let y = &mut v;
        foo(y);
    println!("{:?}", v);
}

alt text

Chúng ta có lỗi, vì chúng ta đã quy phạm quy tắc trong Rust, vậy quy tắc đó là gì

The rules

  • Thứ nhất: Mọi scope của borrowing phải thấp hơn scope của original
  • Thứ hai: Chúng ta chỉ được quyền borrowing từ trong 2 loại dưới đây, không được borrowing đồng thời cả 2:
    • Một hoặc nhiều &T resources.
    • Chỉ duy nhất một và chỉ một mutable reference (&mut T).

Quay trở lại ví dụ trên:

 fn foo(v: &mut Vec<i32>) {
    v.push(5);
}

fn main() {
    let mut v: Vec<i32> = vec![1, 2, 3];
    let y = &mut v;
    foo(y);
    println!("{:?}", v);
}

Compile errors:

error[E0502]: cannot borrow `v` as immutable because it is also borrowed as mutable
let y = &mut v;
              - mutable borrow occurs here
println!("{:?}", v);
                 ^ immutable borrow occurs here

Chúng ta đã qui phạm quy tắc thứ 2: Chỉ duy nhất một và chỉ một mutable reference (&mut T). Nên chúng ta không được phép borrowing dạng &T. Nếu chú ý, có một thông báo lỗi

}
- mutable borrow ends here

Nếu mô tả về scopes thì nó giống như thế này

 fn foo(v: &mut Vec<i32>) {
    v.push(5);
}

fn main() {
    let mut v: Vec<i32> = vec![1, 2, 3];
    let y = &mut v;     // -+ &mut borrow y start here
                        // |
                        // |
    foo(y);             // |
                        // |
                        // |
    println!("{:?}", v);// -+ try to  borrow v here
}                       // -+ &mut borrow y end here

=> Conflict: borrow v trong khi y vẫn đang tồn tại.

Nhưng nếu thêm scope cho biến y

 fn foo(v: &mut Vec<i32>) {
    v.push(5);
}

fn main() {
    let mut v: Vec<i32> = vec![1, 2, 3];
   {
    let y = &mut v;     // -+ &mut borrow y start here
                        // |
                        // |
    foo(y);             // |
                        // |
                        // |
  }                     // -+ &mut borrow y end here
    println!("{:?}", v);// -+ try to  borrow v here
}                       

Không có lỗi khi compile đoạn code trên.

Use after free

Tất cả các refereneces điều phải có scope thấp hơn giá trị original. Khi compile Rust sẽ check xem rule này có đúng không. Như ví dụ dưới đây:

let y: &i32;
{
    let x = 5;
    y = &x;
}

println!("{}", y);

Nhưng khi compile, chúng ta nhận được lỗi:
error: `x` does not live long enough

Nghĩa là, x chỉ live tới cuối }, nhưng y lại live dài hơn, nếu bỏ {} thì sao, vẫn lỗi trên. Để fix vấn đề này chúng ta cần cho x live hơn y

let x = 5;
let y: &i32;
y = &x;

println!("{}", y);

2. Lifetimes

Lifetimes được dùng ở Rust để kiểm tra xem việc borrowing 1 resource là valid hay invalid. Ví dụ:

// Lifetimes are annotated below with lines denoting the creation
// and destruction of each variable.
// `i` has the longest lifetime because its scope entirely encloses 
// both `borrow1` and `borrow2`. The duration of `borrow1` compared 
// to `borrow2` is irrelevant since they are disjoint.
fn main() {
    let i = 3; // Lifetime for `i` starts. ────────────────┐
    //                                                     │
    { //                                                   │
        let borrow1 = &i; // `borrow1` lifetime starts. ──┐│
        //                                                ││
        println!("borrow1: {}", borrow1); //              ││
    } // `borrow1 ends. ──────────────────────────────────┘│
    //                                                     │
    //                                                     │
    { //                                                   │
        let borrow2 = &i; // `borrow2` lifetime starts. ──┐│
        //                                                ││
        println!("borrow2: {}", borrow2); //              ││
    } // `borrow2` ends. ─────────────────────────────────┘│
    //                                                     │
}   // Lifetime ends. ─────────────────────────────────────┘

Lifetimes với functions

fn bar<'a>(x: &'a i32) {
}

Lifetimes với Structs

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

fn main() {
    let y = &5; // this is the same as `let _y = 5; let y = &_y;`
    let f = Foo { x: y };

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

impl Blocks

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

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

fn main() {
    let y = &5; // this is the same as `let _y = 5; let y = &_y;`
    let f = Foo { x: y };

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

Kết luận

  • Mọi scope của borrowing phải thấp hơn scope của original
  • Chúng ta chỉ được quyền borrowing từ trong 2 loại dưới đây, không được borrowing đồng thời cả 2:
    • Một hoặc nhiều &T resources.
    • Chỉ duy nhất một và chỉ một mutable reference (&mut T).
Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Giang Nguyen

20 bài viết.
34 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
30 5
(Ảnh) Hai ngày nay mình đã tìm hiểu về Amazon S3, Cloudfront và Letsencrypt để xây dựng 2 trang web static, thứ nhất là trang chủ của (Link) và t...
Giang Nguyen viết 1 năm trước
30 5
White
9 0
Đôi dòng về ID3 (Ảnh) Nếu bạn nào chưa biết thì có thể đọc phần này, hoặc biết rồi thì có thể next tới phần kế tiếp nhé. 1. Giới thiệu Như hì...
Giang Nguyen viết 12 tháng trước
9 0
White
8 0
Type Result Type trên dùng để làm gì? Result được dùng cho những trường hợp chúng ta muốn return lại một giá trị nào đó (Ok) hoặc propagating erro...
Giang Nguyen viết 11 tháng trước
8 0
Bài viết liên quan
White
3 3
Hôm nay tự viết lại cái note này về quá trình học Rust, xem như là tự giúp mình nhớ sâu và rõ hơn về Rust. Note này sẽ ngắn gọn hơn bản (Link). Sơ...
Giang Nguyen viết 1 năm trước
3 3
White
0 0
fn plus_5(x: Option) i32 { x.unwrap_or(0) + 5 } fn main() { assert_eq(15, plus_5(Some(10))); } Compile không lỗi, thay đổi chút xíu nhỏ, ...
Giang Nguyen viết hơn 1 năm trước
0 0
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
20 bài viết.
34 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!