✅
どっちを使う?Rustの&strとStringの違いを図解で学ぼう
Rustの文字列型には &str
と String
の2種類が存在します。
「どっちを使えばいいの?」「どう違うの?」と悩んだことはありませんか?
この記事では、所有権・可変性・メモリ構造などの観点から、両者の違いをわかりやすく解説します。
&str
とは?
&strは「文字列スライス」と呼ばれ、自分では文字列の実体を持たず、メモリ上の他の場所にある文字列データを参照するだけの軽量な型です。
let greeting: &str = "hello";
この場合、"hello"
という文字列はプログラムのバイナリ(静的領域)に埋め込まれており、&str
はそれをポインタと長さで参照しています。
- 所有権を持たない
- 不変(immutable)
- 軽量
- よく関数の引数などに使われる
String
とは?
String
はヒープ上に確保された文字列の所有者です。可変で、長さを変更したり、新しい文字列を構築したりできます。
let mut name: String = String::from("Alice");
name.push_str(" Smith");
- 所有権を持つ
- 可変(mutable)
- ヒープ上にデータを持つ
-
to_string()
で簡単に作れる
メモリ構造の違い
Rustでは、スタックとヒープという2つのメモリ領域を使い分けています。それぞれの型がどのようにこれらを使っているかが、&str
とString
の違いの鍵になります。
&str
のメモリ構造:参照型(スライス)
let s: &str = "hello";
何が起こっているか
-
"hello"
はコンパイル時に静的メモリ領域に埋め込まれます(.rodata
セクションなど)。 -
s
はスタック上に作られる ポインタと長さ(slice構造) です。 -
s
自身は軽量で、文字列の所有権を持ちません。
図解
特徴まとめ
項目 | 説明 |
---|---|
所有権 | なし(借用) |
メモリ場所 | 参照先に依存(リテラルなら静的メモリ) |
サイズ変更 | 不可(長さは固定) |
開放タイミング | 所有者に従う(&str 自身は所有者でない) |
String
のメモリ構造:所有型(ヒープ確保)
let s: String = String::from("hello");
何が起こっているか
-
"hello"
というデータはヒープに動的確保されます。 -
s
はスタック上に、以下の3つの情報を持ちます。- ポインタ(ヒープ上の先頭アドレス)
- 長さ(len)(文字数、バイト単位)
- 容量(capacity)(確保されたバイト数)
図解
特徴まとめ
項目 | 説明 |
---|---|
所有権 | あり(Dropで自動解放) |
メモリ場所 | 実体はヒープ、管理情報はスタック |
サイズ変更 | 可能(容量の範囲内なら追加コストなし) |
解放タイミング | スコープを抜けたときに自動で Drop
|
相互変換
&str → String
let s: &str = "hello";
let owned: String = s.to_string();
String → &str
let s: String = String::from("hello");
let slice: &str = &s;
使用例
fn greet(name: &str) {
println!("Hello, {name}!");
}
fn main() {
let s1 = "world"; // &'static str
let s2 = String::from("Rustacean"); // String
greet(s1); // OK
greet(&s2); // OK: &String は自動で &str に変換される
}
比較まとめ
特徴 | &str |
String |
---|---|---|
所有権 | なし | あり |
可変性 | 不変 | 可変 |
メモリ位置 | 静的 or 他のヒープ | 自前のヒープ |
サイズ変更 | 不可 | 可 |
コピーコスト | 軽量 | 重い(Clone or ムーブ) |
おわりに
Rustでは「所有権」や「借用」という概念が重要で、&str
とString
はその代表例です。
どちらを使うかは用途によって変わりますが、基本は次のように覚えておけばOKです。
- 変更しない & 借用するなら
&str
- 所有して操作したいなら
String
Discussion