TL; DR: Thay vào đó &str
, người ta có thể sử dụng &[T]
hoặc &T
cho phép mã chung chung hơn.
Một trong những lý do chính để sử dụng a String
hoặc a Vec
là vì chúng cho phép tăng hoặc giảm dung lượng. Tuy nhiên, khi bạn chấp nhận một tham chiếu không thay đổi, bạn không thể sử dụng bất kỳ phương pháp thú vị nào trên Vec
hoặc String
.
Việc chấp nhận a &String
, &Vec
hoặc &Box
cũng yêu cầu đối số được cấp phát trên heap trước khi bạn có thể gọi hàm. Việc chấp nhận a &str
cho phép một ký tự chuỗi (được lưu trong dữ liệu chương trình) và chấp nhận một &[T]
hoặc &T
cho phép một mảng hoặc biến được phân bổ theo ngăn xếp. Phân bổ không cần thiết là một tổn thất hiệu suất. Điều này thường được hiển thị ngay lập tức khi bạn cố gắng gọi các phương thức này trong một bài kiểm tra hoặc một main
phương thức:
awesome_greeting(&String::from("Anna"));
total_price(&vec![42, 13, 1337])
is_even(&Box::new(42))
Một xem xét hiệu suất là &String
, &Vec
và &Box
giới thiệu một lớp không cần thiết về mình như bạn phải dereference sự &String
để có được một String
và sau đó thực hiện một dereference thứ hai để kết thúc tại &str
.
Thay vào đó, bạn nên chấp nhận một chuỗi slice ( &str
), một slice ( &[T]
) hoặc chỉ một tham chiếu ( &T
). A &String
, &Vec<T>
hoặc &Box<T>
sẽ tự động bị ép buộc đối với a &str
, &[T]
hoặc &T
, tương ứng.
fn awesome_greeting(name: &str) {
println!("Wow, you are awesome, {}!", name);
}
fn total_price(prices: &[i32]) -> i32 {
prices.iter().sum()
}
fn is_even(value: &i32) -> bool {
*value % 2 == 0
}
Bây giờ bạn có thể gọi các phương thức này với nhiều loại kiểu hơn. Ví dụ, awesome_greeting
có thể được gọi với một chuỗi đen ( "Anna"
) hoặc một phân bổ String
. total_price
có thể được gọi với một tham chiếu đến một mảng ( &[1, 2, 3]
) hoặc một phân bổ Vec
.
Nếu bạn muốn thêm hoặc xóa các mục khỏi String
hoặc Vec<T>
, bạn có thể lấy tham chiếu có thể thay đổi ( &mut String
hoặc &mut Vec<T>
):
fn add_greeting_target(greeting: &mut String) {
greeting.push_str("world!");
}
fn add_candy_prices(prices: &mut Vec<i32>) {
prices.push(5);
prices.push(25);
}
Cụ thể cho các lát, bạn cũng có thể chấp nhận một &mut [T]
hoặc &mut str
. Điều này cho phép bạn thay đổi một giá trị cụ thể bên trong lát, nhưng bạn không thể thay đổi số lượng mục bên trong lát (có nghĩa là nó rất hạn chế đối với chuỗi):
fn reset_first_price(prices: &mut [i32]) {
prices[0] = 0;
}
fn lowercase_first_ascii_character(s: &mut str) {
if let Some(f) = s.get_mut(0..1) {
f.make_ascii_lowercase();
}
}
&str
là tổng quát hơn (như trong: áp đặt ít hạn chế hơn) mà không giảm khả năng"? Ngoài ra: tôi nghĩ điểm 3 thường không quan trọng lắm. Thông thườngVec
s vàString
s sẽ sống trên ngăn xếp và thường thậm chí ở đâu đó gần khung ngăn xếp hiện tại. Ngăn xếp thường nóng và tham chiếu sẽ được phân phát từ bộ đệm CPU.