Sửa lỗi Rust error[E0308]: mismatched types — String vs &str

beginner🦀 Rust2026-04-15| Rust 1.x (tất cả phiên bản), mọi hệ điều hành — Linux, macOS, Windows

Error Message

error[E0308]: mismatched types: expected struct `String`, found `&str`
#rust#string#types#conversion

Lỗi

error[E0308]: mismatched types: expected struct `String`, found `&str`

Hoặc ngược lại:

error[E0308]: mismatched types: expected `&str`, found struct `String`

Đây là một trong những rào cản đầu tiên mà các lập trình viên Rust mới hay gặp phải. String&str trông gần như giống nhau trong mã nguồn — nhưng với trình biên dịch, chúng là hai kiểu hoàn toàn khác nhau. Rust sẽ không tự động chuyển đổi giữa chúng.

Nguyên Nhân

String được cấp phát trên heap và có quyền sở hữu — nó mang dữ liệu theo cùng. &str chỉ là một tham chiếu mượn vào dữ liệu chuỗi tồn tại ở nơi khác. Hai thứ khác nhau. Trình biên dịch áp đặt ranh giới này một cách nghiêm ngặt, vì vậy sự không khớp sẽ xảy ra lỗi lúc biên dịch, không phải lúc chạy.

Các tình huống phổ biến dễ mắc lỗi này:

  • Truyền một chuỗi literal (&str) vào hàm mong đợi String
  • Truyền một String có quyền sở hữu vào chỗ hàm cần &str
  • Gán một literal vào trường struct có kiểu là String
  • Trả về một literal từ hàm có kiểu trả về là String

Cách Sửa Nhanh — Chọn Trường Hợp Của Bạn

Trường hợp 1: Bạn có &str, cần String

Gọi .to_string(), .to_owned(), hoặc String::from():

// Trước — lỗi biên dịch
fn greet(name: String) {
    println!("Hello, {}!", name);
}

fn main() {
    greet("Alice"); // error: expected String, found &str
}

// Sau — đã sửa
fn main() {
    greet("Alice".to_string());
    // hoặc
    greet("Alice".to_owned());
    // hoặc
    greet(String::from("Alice"));
}

Cả ba đều cho kết quả giống nhau. Hầu hết code Rust thực tế bạn thấy đều dùng .to_string().

Trường hợp 2: Bạn có String, cần &str

Mượn bằng & hoặc gọi .as_str():

fn print_name(name: &str) {
    println!("{}", name);
}

fn main() {
    let name = String::from("Alice");

    print_name(&name);         // deref coercion: &String → &str
    print_name(name.as_str()); // tường minh, hiệu quả như nhau
    print_name(&name[..]);     // cú pháp slice — hoạt động nhưng ít dùng
}

&name hoạt động được vì Rust tự động áp dụng deref coercion từ &String sang &str. Không có việc sao chép dữ liệu. Đây là cách tiếp cận chuẩn mực.

Trường hợp 3: Trường struct có kiểu String, bạn đang gán một literal

struct Config {
    host: String,
}

// Lỗi
let c = Config { host: "localhost" }; // expected String, found &str

// Đã sửa
let c = Config { host: "localhost".to_string() };
let c = Config { host: String::from("localhost") };

Trường hợp 4: Kiểu trả về của hàm là String nhưng bạn đang trả về một literal

// Lỗi
fn get_default() -> String {
    "unknown" // expected String, found &str
}

// Đã sửa
fn get_default() -> String {
    "unknown".to_string()
    // hoặc: String::from("unknown")
}

Trường hợp 5: Đẩy các literal vào Vec

// Lỗi
let mut names: Vec = Vec::new();
names.push("Alice"); // expected String, found &str

// Đã sửa
names.push("Alice".to_string());
names.push(String::from("Bob"));

Tốt Hơn Nữa: Nhận &str Trong Các Hàm Của Bạn

Nếu bạn kiểm soát được chữ ký hàm và không cần lưu trữ hay chỉnh sửa chuỗi, hãy đổi kiểu tham số thành &str thay vì String. Nó chấp nhận cả literal lẫn chuỗi có quyền sở hữu thông qua coercion — không cần chuyển đổi gì ở chỗ gọi hàm:

// Hạn chế — người gọi phải truyền một String có quyền sở hữu
fn greet(name: String) { ... }

// Linh hoạt — hoạt động với cả literal lẫn &String
fn greet(name: &str) { ... }

// Cả hai cách gọi này đều biên dịch được mà không cần .to_string():
greet("Alice");         // &str literal — hoạt động
greet(&owned_string);   // &String — hoạt động qua coercion

Hướng dẫn API của Rust khuyến nghị dùng &str cho các tham số chuỗi chỉ đọc. Đây là thay đổi nhỏ giúp người gọi tránh được các lần cấp phát bộ nhớ không cần thiết.

Kiểm Tra Kết Quả

Biên dịch lại dự án:

cargo build

Biên dịch thành công trông như sau:

   Compiling myproject v0.1.0
    Finished dev [unoptimized + debuginfo] target(s) in 0.42s

Không có error[E0308] trong kết quả đầu ra nghĩa là lỗi không khớp kiểu đã được khắc phục. Để kiểm tra nhanh một file, rustc main.rs cũng hoạt động tốt.

Khi Nào Dùng Gì

  • .to_string() — lựa chọn hàng đầu để chuyển &str sang String. Dễ đọc, chuẩn mực.
  • .to_owned() — kết quả tương tự, hoạt động trên bất kỳ kiểu Borrow nào. Một số style guide ưa dùng hơn.
  • String::from() — khởi tạo tường minh. Tốt khi bạn muốn rõ ràng về ý định.
  • &string hoặc .as_str() — chuyển từ owned sang borrowed. Không tốn chi phí, không cấp phát bộ nhớ.

Tham Khảo Nhanh

// &str → String
let s: String = "hello".to_string();
let s: String = "hello".to_owned();
let s: String = String::from("hello");
let s: String = format!("hello"); // dùng khi bạn cần định dạng chuỗi

// String → &str
let r: &str = &owned;          // deref coercion
let r: &str = owned.as_str();  // tường minh
let r: &str = &owned[..];      // full slice

Related Error Notes