RustのcharとStringと&strがすごく難しい。さすが学習難易度が高いRust。それでも、たぶん慣れの問題で、覚えて慣れるのが良い方法かも。
charとStringと&strの関係
Rustでは、Stringと&strがたくさん出てくる。この三者の違いを一言で語ってみよう。
-
String
は文字列を表すことができる。 -
&str
はUTF-8の配列のスライス(&[u8])であり文字列リテラルを表すのに使われる。 -
char
はユニコードの1文字を表現できる。
Stringと&strの関係
文字列リテラルは&strである。
let s1: &str = "abc";
println!("{}", s1); // abc
Stringと&strは容易に変換が可能。
// &str → String
let s1: String = String::from("abc");
println!("{}", s1);
&str → String → &strと変換する場合。
let s1: &str = "abc";
let s2: String = String::from(s1); // &str -> String
let s3: &str = &s2; // String -> &str
println!("{}", s3);
このように、&strとStringは比較的簡単に変換できる。
charをStringに変換
charをStringに変換するには、to_stringメソッドが使える。
let c: char = 'a';
let cs: String = c.to_string();
println!("{}", &cs); // → a
Vec<char>をStringに変換
サイズ可変な配列であるcharのベクタ型であるVec<char>。これをStringに変換するには、iter().collect()を使う。
// Vec<char>を初期化
let c = vec!['a', 'b', 'c'];
// Vec<char>をStringに変換
let cs: String = c.iter().collect();
println!("{}", &cs); // → abc
Stringや&strをVec<char>に変換
&strをVec<char>に変換してみる。
// &str → Vec<char>
let cs: Vec<char> = "abcd".chars().collect();
// Vec<char>であれば、添字アクセス可能
let c1: char = cs[0];
let c2: char = cs[1];
println!("{}-{}", c1.to_string(), c2.to_string());
同じように、StringをVec<char>に変換することも同様の方法で可能。
let s = String::from("abc");
let cs: Vec<char> = s.chars().collect();
let c1: char = cs[0];
println!("{}", c1.to_string());
&[char]をStringに変換する場合も、iter().collect()でいける。
let cs: &[char] = &['a', 'b', 'c'];
let s: String = cs.iter().collect();
println!("{}", s); // abc
String に charを追加する
let mut s = String::from("abc");
s.push('d');
println!("{}", s); // abcd
&str に char を追加したい
&strは残念ながら変更できないので一度Stringにしてからcharを追加する。
let s = "abc";
let mut ss = String::from(s);
ss.push('d');
println!("{}", ss); // abcd
Vec<char>とVec<char>を足す
mut Vec<char>であれば、extendで追記が可能。
let mut a: Vec<char> = "abc".chars().collect();
let b: Vec<char> = "def".chars().collect();
a.extend(b.iter()); // bの内容をaに追加
let c: String = a.iter().collect();
println!("{}", c); // abcdef
似たような感じだけど、Vec.append()も使える。
let mut a: Vec<char> = "abc".chars().collect();
let b: Vec<char> = "def".chars().collect();
a.append(&mut b.clone());
println!("{}", a.iter().collect::<String>()); // abcdef
Stringに変換してconcatするという手もある、冗長だけど。
let a: Vec<char> = "abc".chars().collect();
let b: Vec<char> = "def".chars().collect();
let c: String = [
a.iter().collect::<String>(),
b.iter().collect::<String>()
].concat();
println!("{}", c); // abcdef
上記で、Vec<char>のaとbの内容を変更したくない場合は、新しい変数cを作成する。
let a = "abc".chars().collect::<Vec<char>>();
let b = "def".chars().collect::<Vec<char>>();
// c = a + b
let mut c: Vec<char> = Vec::new();
c.append(&mut a.clone());
c.append(&mut b.clone());
// print(c)
let s = c.iter().collect::<String>();
println!("{}", s); // abcdef
上記と全く同じで別の方法。好みにもよるけど、extend()使った方がシンプルな感じがする。
let a = "abc".chars().collect::<Vec<char>>();
let b = "def".chars().collect::<Vec<char>>();
// c = a + b
let mut c: Vec<char> = Vec::new();
c.extend(a.iter());
c.extend(b.iter());
// print(c)
let s = c.iter().collect::<String>();
println!("{}", s); // abcdef
コメントで、chain()を使う方法を教えていただきました。この方法も良いですね。
let a = "abc".chars().collect::<Vec<char>>();
let b = "def".chars().collect::<Vec<char>>();
// String が必要な場合
let s = a.iter().chain(b.iter()).collect::<String>();
println!("{}", s); // abcdef
// Vec<&char> が必要な場合
let c = a.iter().chain(b.iter()).collect::<Vec<&char>>();
println!("{:?}", c); // ['a', 'b', 'c', 'd', 'e', 'f']
// Vec<char>が必要で、aとbの所有権を奪う場合
let d: Vec<char> = a.into_iter().chain(b.into_iter()).collect();
println!("{:?}", d); // ['a', 'b', 'c', 'd', 'e', 'f']
参考
『Rustの書きかた・作りかた』という本を書きました。良かったらそちらも見てみてください!!
その他、文字列操作で役立つリンク