[Rust] Tìm hiểu về lifetime elision
Hôm nay, chúng ta sẽ tìm hiểu một chút về lifetime elision
trong Rust. Hiểu được luật của nó giúp chúng ta không bổi rối khi làm việc với Lifetime
ĐỊNH NGHĨA
Là một tính năng của Rust compiler mà giúp chúng ta biến đổi lifetime từ đầu vào tới đầu ra. Nói một cách khác, đây là một số rules giúp chúng ta biết cách khai báo ký hiệu
lifetime 'a
KHI NÀO CHÚNG TA QUAN TÂM TỚI LIFETIME
Khi một hàm của chúng ta nhận vào danh sách các tham chiếu và cũng trả về tham chiếu thì chúng ta cần quan tâm đến nó
Sau đây là một ví dụ kinh điển
fn compare(x: &u32, y: &u32) -> &u32 {
if x > y {
x
} else {
y
}
}
LUẬT
Có 3 rules mà chúng ta có thể bắt đầu.
- Mỗi một đầu vào dạng tham chiếu sẽ có 1 lifetime của chính nó.
- Nếu như đầu vào chỉ có 1 tham số là lifetime, nó mặc định gán lifetime tới tất cả các đầu ra
- Trong trường hợp có nhiều tham số đầu vào vào, một trong đầu vào là
&self
hoặc&mut self
. Lifetime này sẽ được gán tới tất cả đầu ra
VÍ DỤ
Rule 1
fn update(&str, &str) -> &str
Trong hàm trên chúng ta có thể viết như sau theo luật 1
fn update<'a, 'b>(&'a str, &'bstr) -> &'str
Rule 2
fn update(&str) -> (&str, &str)
Theo luật 2 nó chỉ có một đầu vào nên mặc nhiên lifetime của đầu vào này sẽ là lifetime của đầu ra
fn update(&'a str) -> (&'a str, &'a str)
Rule 3
fn update(&mut self, &str, i32) -> (&str, &str)
Trong trường hợp này ta có nhiều tham số đầu vào mà 1 trong các tham số đầu ra là &mut self. Chiếu theo luật 3 ta có thể viết
fn update<'a, 'b>(&'a mut self, &'b str, i32) -> (&'a str, &'a str)
Thằng &'b str
nó có lifetime của riêng nó là 'b
LIFETIME VỚI STRUCT
struct ArrayProcessor {
data: &'[i32],
}
Lifetime elision sẽ giúp ta có thể viết
struct ArrayProcessor<'a> {
data: &'a [i32],
}
fn main(){
let mut data = ArrayProcessor(data: &[1,2,3]);
}
Khai báo lifetime trong struct thể hiện biến data mượn có lifetime ít nhất phải bằng lifetime của struct chứa nó.
Viết thêm hàm impl impl ArrayProcessor {}
compiler sẽ báo lỗi và yêu cầu chúng ta khai báo lifetime
struct ArrayProcessor<'a> {
data: &'a [i32],
}
impl<'a> ArrayProcesssor<'a>{
fn update(&mut self, new_data: &'a[i32]) -> &[i32] {
let prev_data = self.data;
self.data = new_data;
prev_data
}
}
fn main(){
let mut data = ArrayProcessor(&[1,2,3]);
let prev_data = data.update(&[4,5,6])
}
Chúng ta sẽ viết lại như sau theo 3 rules trên
impl<'a> ArrayProcesssor<'a>{
fn update<'b>(&'b mut self, new_data: &'a [i32]) -> &'b [i32] {
let prev_data = self.data;
self.data = new_data;
prev_data
}
}
Have fun!