S-JIS[2024-09-26/2025-07-06] 変更履歴

Rust String構造体

Ruststd::string::String構造体のメモ。


概要

Rustで文字列を扱うのがString構造体。
String構造体はstd::preludeに含まれているので、useしなくても使える。

内部ではVec<u8>(バイト列)でUTF-8を保持している。末尾のnul文字は無いらしい。


let s = String::new();	// 空の文字列
let s = String::from("abc");
let s = "abc".to_string();

Stringは変更可能な文字列である。つまり別の文字列を付け加えたりすることが出来る。

    let mut s = String::from("abc");
    s.push_str("def");	// 末尾に文字列を追加する
    println!("{}", s);

ただし、Stringの中身を変更する場合は、Stringを保持している変数にmut(mutable(変更可能))を付けておく必要がある。
(mutが付いていない変数は不変という扱いになり、インスタンスの中身を変更することは出来ない)


関数(やメソッド)の引数が&strの場合、そこに&Stringを渡すことが出来る。

fn example1(s: &str) {
    println!("{}", s);
}
    let s: String = String::from("abc");
    example1(&s);

文字列を構造体(やタプル形式の列挙型)のフィールドに保持したい場合、Stringを使うのが一般的なようだ。[2025-07-06]

struct MyStruct {
    value: String,
}

impl MyStruct {
    fn new(value: String) -> MyStruct {
        MyStruct { value }
    }
}

(Stringは変更可能な文字列なので、フィールドには「変更できない&str」を保持したくなるかもしれないが、&strは参照なので、フィールドに保持しようとするとライフタイムの問題が出てきてややこしくなる)


StringとOption<String>の両方を受け取る関数の例

Rustでは関数のオーバーロードを作れない(引数の型が異なる同名関数を作れない)が、ひとつの関数の引数でStringとOption<String>のどちらでも受け取れるようにすることが出来る。

(何故そんなことをしたいかと言うと、RustではStringにnullを保持できないので、普段はStringで受け取りつつ、何も無いときはNoneが渡せると便利だから)

fn example2<T: Into<Option<String>>>(s: T) {
    let s: Option<String> = s.into();
    match s {
        Some(s) => println!("{}", s),
        None => println!("None"),
    }
}
    example2(String::from("abc"));
    example2(None);

Option<&str>とOption<&String>の両方を受け取る関数の例

関数の引数を&strで定義しておくと&Stringを渡せるが、
関数の引数がOption<&str>の場合はOption<&String>を渡すことは出来ない。[2025-07-06]

AsRefを使うと、ひとつの関数でOption<&str>とOption<&String>のどちらも受け取れる。

fn example3<S: AsRef<str>>(value: Option<S>) {
    match value {
        Some(value) => {
            let s = value.as_ref(); // &str
            println!("value={}", s);
        }
        None => println!("value=None"),
    }
}
    example3(Some("abc"));

    let s = "def".to_string();
    example3(Some(&s));

または以下のように書くことも出来る。

fn example3(value: Option<impl AsRef<str>>) {
    〜
}

(ジェネリクスを使う方式より新しいらしい)


ジェネリクスを使う方式と直接書く方式には、以下のような違いがある。


&strとStringの両方を受け取る関数の例

AsRefまたはIntoを使って、ひとつの関数で&strとString(ついでに&String)のいずれも受け取れるように出来る。[2025-07-06]


AsRefを使うと、&strとして取得される。

fn example4_1(value: impl AsRef<str>) {
    let s = value.as_ref(); // &str
    println!("value={}", s);
}

Intoを使うと、Stringとして取得できる。

fn example4_2(value: impl Into<String>) {
    let s: String = value.into();
    println!("value={}", s);
}

Stringのinto()は自分自身を返すので、コピーは発生しない。
(&Stringのinto()はクローンを返す)


    example4_x("abc"); // &str

    let s = "def".to_string();
    example4_x(&s); // &String
    example4_x(s);  // String

引数をStringで渡すと、所有権が奪われる。
&Stringや&strで渡した場合は、所有権は奪われない。


stdへ戻る / Rustへ戻る / 技術メモへ戻る
メールの送信先:ひしだま