どっちを使う?Rustの&strとStringの違いを図解で学ぼう

に公開

Rustの文字列型には &strString の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つのメモリ領域を使い分けています。それぞれの型がどのようにこれらを使っているかが、&strStringの違いの鍵になります。

&str のメモリ構造:参照型(スライス)

let s: &str = "hello";

何が起こっているか

  • "hello"コンパイル時に静的メモリ領域に埋め込まれます(.rodataセクションなど)。
  • s はスタック上に作られる ポインタと長さ(slice構造) です。
  • s 自身は軽量で、文字列の所有権を持ちません。

図解

特徴まとめ

項目 説明
所有権 なし(借用)
メモリ場所 参照先に依存(リテラルなら静的メモリ)
サイズ変更 不可(長さは固定)
開放タイミング 所有者に従う(&str自身は所有者でない)

String のメモリ構造:所有型(ヒープ確保)

let s: String = String::from("hello");

何が起こっているか

  • "hello" というデータはヒープに動的確保されます。
  • s はスタック上に、以下の3つの情報を持ちます。
    1. ポインタ(ヒープ上の先頭アドレス)
    2. 長さ(len)(文字数、バイト単位)
    3. 容量(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では「所有権」や「借用」という概念が重要で、&strStringはその代表例です。

どちらを使うかは用途によって変わりますが、基本は次のように覚えておけばOKです。

  • 変更しない & 借用するなら &str
  • 所有して操作したいなら String
コラボスタイル Developers

Discussion