Bạn có chắc chắn muốn xóa bài viết này không ?
Bạn có chắc chắn muốn xóa bình luận này không ?
References and borrowing, lifetimes in Rust
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à &T
là reference
, 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
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ó {
và }
, 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);
}
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)
.
- Một hoặc nhiều
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)
.
- Một hoặc nhiều






