[Hướng dẫn Rust] 4.3. Sở hữu và Hàm
Bài viết này đi sâu vào cơ chế sở hữu (ownership) của Rust khi làm việc với hàm. Chúng ta sẽ tìm hiểu về việc truyền tham số (di chuyển hay sao chép), cách quyền sở hữu chuyển đổi qua giá trị trả về, và giải pháp tạm thời khi muốn sử dụng dữ liệu mà không mất quyền sở hữu.
![[Hướng dẫn Rust] 4.3. Sở hữu và Hàm](https://news1.webcdn.lehuy.net/articles/2026/04/36409776dccdd3188f3bb12722e0165c.png)
Sau khi đã nắm được các khái niệm lập trình chung của Rust, chúng ta đến với phần quan trọng nhất của ngôn ngữ này đó là sở hữu (ownership). Đây là một khái niệm khá khác biệt so với các ngôn ngữ khác và nhiều người mới bắt đầu thường thấy khó khăn khi học. Chương này nhằm giúp các bạn làm chủ hoàn toàn tính năng này.
Chương này gồm ba phần nhỏ:
- Sở hữu: Bộ nhớ Stack vs Bộ nhớ Heap
- Quy tắc Sở hữu, Bộ nhớ và Cấp phát
- Sở hữu và Hàm (bài viết này)
Nếu bạn thấy bài viết hữu ích, hãy like, bookmark và theo dõi để tiếp tục đồng hành cùng series này.
Truyền giá trị cho hàm
Về mặt ngữ nghĩa, việc truyền một giá trị cho hàm tương tự như việc gán giá trị đó cho một biến. Nói một cách ngắn gọn: việc truyền tham số cho hàm hoạt động y hệt như gán biến.
Cụ thể hơn, việc truyền giá trị cho hàm sẽ gây ra một trong hai hành vi: di chuyển (move) hoặc sao chép (copy).
- Đối với các kiểu dữ liệu triển khai Copy trait, hành vi sao chép sẽ xảy ra, do đó biến gốc không bị ảnh hưởng và vẫn có thể tiếp tục sử dụng.
- Đối với các kiểu dữ liệu không triển khai Copy trait, hành vi di chuyển sẽ xảy ra, biến gốc sẽ bị vô hiệu hóa và không thể sử dụng được nữa.
(Một giới thiệu chi tiết về Copy trait, move và copy đã được trình bày trong bài viết trước 4.2. Quy tắc Sở hữu, Bộ nhớ và Cấp phát, nên tôi sẽ không lặp lại ở đây).
fn main() {
let machine = String::from("6657");
wjq(machine);
let x = 6657;
wjq_copy(x);
println!("x is: {}", x);
}
fn wjq(some_string: String) {
println!("{}", some_string);
}
fn wjq_copy(some_number: i32) {
println!("{}", some_number);
}
-
Đối với biến
machine:Stringlà kiểu dữ liệu phức tạp, được cấp phát trên heap và không triển khai Copy trait.- Khi
machineđược truyền vào hàmwjq, một hành vi di chuyển (move) xảy ra, nghĩa là quyền sở hữu được chuyển từ biếnmachinesang tham số hàmsome_string. - Lúc này, quyền sở hữu của
machineđã bị chuyển đi. Hàmwjqcó thể sử dụng nó bình thường, nhưng biến gốcmachinekhông còn khả dụng nữa. Nếu bạn cố gắng sử dụngmachinesau đó, trình biên dịch sẽ báo lỗi.
-
Đối với biến
x:i32là kiểu dữ liệu cơ bản có kích thước cố định, được cấp phát trên stack và triển khai Copy trait.- Khi
xđược truyền vào hàmwjq_copy, hành vi sao chép (copy) xảy ra, nghĩa là giá trị củaxđược sao chép và truyền vào tham số hàmsome_number. - Vì đây chỉ là sao chép giá trị, biến gốc
xkhông bị ảnh hưởng và vẫn có thể sử dụng sau khi gọi hàm.
-
Đối với biến
some_string:- Phạm vi của nó bắt đầu khi được khai báo ở dòng 10 và kết thúc khi gặp dấu
}ở dòng 12. - Khi ra khỏi phạm vi, Rust tự động gọi hàm
dropđể giải phóng bộ nhớ màsome_stringđang chiếm giữ.
- Phạm vi của nó bắt đầu khi được khai báo ở dòng 10 và kết thúc khi gặp dấu
-
Đối với biến
some_number:- Phạm vi của nó bắt đầu khi được khai báo ở dòng 14 và kết thúc khi gặp dấu
}ở dòng 16. - Không có gì đặc biệt xảy ra khi nó ra khỏi phạm vi, vì các kiểu triển khai Copy trait không gọi
Dropkhi hết phạm vi.
- Phạm vi của nó bắt đầu khi được khai báo ở dòng 14 và kết thúc khi gặp dấu
Giá trị trả về và Phạm vi
Quyền sở hữu cũng được chuyển giao trong quá trình trả về giá trị từ một hàm.
fn main() {
let s1 = give_ownership();
let s2 = String::from("6657");
let s3 = takes_and_gives_back(s2);
}
fn give_ownership() -> String {
let some_string = String::from("machine");
some_string
}
fn takes_and_gives_back(a_string: String) -> String {
a_string
}
-
Hành vi của hàm
give_ownership:- Hàm
give_ownershiptạo ra một biếnStringtên làsome_string, và quyền sở hữu nó thuộc về hàmgive_ownership. - Khi
some_stringđược trả về làm giá trị trả về của hàm, quyền sở hữu được chuyển cho người gọi, cụ thể là biếns1. - Kết quả là
some_stringsẽ không bị drop sau khi ra khỏi phạm vi củagive_ownership, vì quyền sở hữu của nó đã được chuyển chos1.
- Hàm
-
Hành vi của hàm
takes_and_gives_back:- Hàm
takes_and_gives_backnhận một tham sốStringlàa_string. Khi hàm được gọi, quyền sở hữu của đối số truyền vào (s2) được chuyển cho tham số hàma_string. - Khi hàm trả về
a_string, quyền sở hữu lại được chuyển một lần nữa từa_stringsang người gọi, cụ thể là biếns3. - Lúc này,
s2không còn khả dụng nữa, vì quyền sở hữu của nó đã được chuyển chotakes_and_gives_back, và giá trị trả về của hàm được gán chos3.
- Hàm
Quyền sở hữu của một biến luôn tuân theo cùng một mẫu:
- Gán giá trị cho một biến khác gây ra di chuyển (move). Chỉ các kiểu triển khai Copy trait, như các kiểu cơ bản
i32vàf64, mới được sao chép (copy) khi gán. - Khi một biến chứa dữ liệu trên heap ra khỏi phạm vi, giá trị của nó sẽ được dọn dẹp bởi hàm
drop, trừ khi quyền sở hữu của dữ liệu đó đã được di chuyển sang một biến khác.
Để hàm sử dụng giá trị mà không lấy quyền sở hữu
Đôi khi mục đích của mã là để một hàm sử dụng một biến, nhưng bạn không muốn mất quyền sử dụng dữ liệu đó. Trong trường hợp đó, bạn có thể viết như sau:
fn main() {
let s1 = String::from("Hello");
let (s2, len) = calculate_length(s1);
println!("The length of '{}' is {}", s2, len);
}
fn calculate_length(s: String) -> (String, usize) {
let length = s.len();
(s, length)
}
Trong ví dụ này, s1 phải chuyển quyền sở hữu cho s, nhưng khi hàm này trả về, nó cũng trả về s nguyên vẹn và chuyển quyền sở hữu dữ liệu cho s2. Theo cách này, quyền sở hữu của dữ liệu được trả lại cho một biến trong hàm main, cho phép dữ liệu của s1 được sử dụng lại trong main (dù tên biến đã thay đổi).
Cách tiếp cận này quá rắc rối và vụng về. Rust cung cấp một tính năng cho tình huống này gọi là tham chiếu (reference), cho phép một hàm sử dụng một giá trị mà không cần lấy quyền sở hữu của nó. Tính năng này sẽ được giải thích trong bài viết tiếp theo.
Bài viết liên quan

Phần mềm
Anthropic ra mắt Claude Opus 4.7: Nâng cấp mạnh mẽ cho lập trình nhưng vẫn thua Mythos Preview
16 tháng 4, 2026

Công nghệ
Qwen3.6-35B-A3B: Quyền năng Lập trình Agentic, Nay Đã Mở Cửa Cho Tất Cả
16 tháng 4, 2026

Công nghệ
Spotify thắng kiện 322 triệu USD từ nhóm pirate Anna's Archive nhưng đối mặt với bài toán thu hồi
16 tháng 4, 2026
