📝
String vs &str — Rubyにはない文字列の二つの顔
所有する文字列と借用する文字列の違い
Rubyでは文字列はシンプル。String1つ。作って、変えて、渡す。Rustでは文字列が2種類。
String — 所有する文字列
let s = String::from("hello");
let s = "hello".to_string(); // 同じ結果
ヒープに確保される。サイズが変わる。Rubyの通常のStringに最も近い。
&str — 借用する文字列
let s: &str = "hello"; // 文字列リテラルは常に&str
どこかにある文字列データへの参照(スライス)。サイズ変更不可。Rubyのfrozen stringに近い。
いつどちらを使うか
関数の引数 → &str
// ✅ 良い — Stringも&strも受けられる
fn greet(name: &str) {
println!("Hello, {}!", name);
}
greet("world"); // &str渡し
greet(&my_string); // String → &str自動変換
// ❌ 不必要に制限的
fn greet(name: String) { // Stringのみ受付
println!("Hello, {}!", name);
}
構造体のフィールド → String
struct User {
name: String, // 所有する必要があるのでString
}
変換
// &str → String
let owned: String = "hello".to_string();
let owned: String = String::from("hello");
let owned: String = "hello".to_owned();
// String → &str
let borrowed: &str = &owned;
let borrowed: &str = owned.as_str();
文字列連結 — Rubyより面倒
Rubyの"Hello, " + nameや"Hello, #{name}"はシンプル。Rustは少し違う。
// format! — Rubyの"#{}"/sprintfに相当
let msg = format!("Hello, {}!", name);
// +演算子 — 左辺がStringでなければならない
let msg = String::from("Hello, ") + name;
// push_str — Stringに&strを追加
let mut msg = String::from("Hello, ");
msg.push_str(name);
大抵format!を使えばいい。Rubyの文字列補間に最も近い。
なぜ2種類あるか
性能と所有権。&strはコピーなしで既存の文字列データを指す。関数に文字列を渡す時、ヒープ割り当てなしで参照だけ渡せる。これがRustが速い理由の一つ。
キーポイント
1
String = ヒープ確保、可変 / &str = 参照、不変
2
関数の引数は&strで受けるのが慣例 — Stringも自動変換される
3
構造体のフィールドはString — 所有権が必要だから
4
format!()がRubyの文字列補間(#{})に最も近い
メリット
- ✓ 不要な文字列コピーを&strで回避 — 性能のメリット
- ✓ Stringと&strの区別が所有権理解の良い入口
デメリット
- ✗ \"hello\".to_string()のような変換が面倒
- ✗ 構造体に&strを入れるにはライフタイム注釈が必要 — 初心者キラー
ユースケース
APIレスポンスパース — JSONから取り出した文字列の処理
設定ファイル読み込み — 内容はString、パース中は&str