Rustのstd::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は参照なので、フィールドに保持しようとするとライフタイムの問題が出てきてややこしくなる)
Rustでは関数のオーバーロードを作れない(引数の型が異なる同名関数を作れない)が、
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で渡した場合は、所有権は奪われない。
ひとつの関数の引数で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);
関数の引数を&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>>) {
〜
}
(ジェネリクスを使う方式より新しいらしい)
ジェネリクスを使う方式と直接書く方式には、以下のような違いがある。
fn example3<S: AsRef<str>>(s1: Option<S>, s2: Option<S>)fn example3(s1: Option<impl AsRef<str>>, s2: Option<impl
AsRef<str>>)Intoを使って、ひとつの関数でOption<&str>とOption<String>(ついでにOption<&String>)のいずれも受け取れるように出来る。[2025-08-28]
fn example5(value: Option<impl Into<String>>) {
let s /* : Option<String> */ = value.map(Into::into);
println!("value={:?}", s);
}
example5(Some("abc")); // Option<&str>
let s = "def".to_string();
example5(Some(&s)); // Option<&String>
example5(Some(s)); // Option<String>
ただ、Noneを渡す場合は型を明示する必要がある。
let none: Option<String> = None;
example5(none);
example5(None::<String>);