Rustで文字列String, &strを連結(format!, push_strなど)
Rustで文字列Stringや文字列スライス&strを連結(結合)するには、format!マクロや、push_str、+, +=演算子などを使う。
文字列のベクタVec<String>などを一つの文字列Stringに連結する方法については以下の記事を参照。
String, &strを連結してStringを生成: format!
format!マクロを使うと、複数のStringや&strを連結して新たなStringを生成できる。
第一引数の文字列リテラル内のプレースホルダー{}に第二引数以降の値が挿入される。新たなStringが生成され、元のString, &strはそのまま。
let s_string = String::from("abc");
let s_str = "XYZ";
let s = format!("{}_123_{}", s_string, s_str);
assert_eq!(s, "abc_123_XYZ");
assert_eq!(s_string, "abc");
assert_eq!(s_str, "XYZ");
String同士や&str同士でも問題ない。3個以上の場合も同様。
シンプルに連結したい場合は第一引数を"{}{}"のようにすればよい。
let s = format!("{}{}", s_string, s_str);
assert_eq!(s, "abcXYZ");
文字charも扱える。{}内に変数名を直接記述することも可能。
let c = '@';
let s = format!("{s_string}{c}{s_str}");
assert_eq!(s, "abc@XYZ");
既存のStringにString, &strを連結
format!マクロは新たなStringを生成するが、既存のStringに別のString, &strを連結することもできる。
push_str
Stringのpush_strメソッドを使う。
push_strメソッドの引数は文字列スライス&str。呼び出し元のStringはミュータブルでなければならないので注意。
let mut s_string = String::from("abc");
let s_str = "XYZ";
s_string.push_str(s_str);
assert_eq!(s_string, "abcXYZ");
assert_eq!(s_str, "XYZ");
&strではなくStringを連結したい場合は、参照を指定する。
let mut s_string1 = String::from("abc");
let s_string2 = String::from("XYZ");
s_string1.push_str(&s_string2);
assert_eq!(s_string1, "abcXYZ");
assert_eq!(s_string2, "XYZ");
push_strは破壊的処理。元のStringをそのまま残しておきたい場合は、cloneでクローンを生成してpush_strを実行すればよい。
let s_string = String::from("abc");
let s_str = "XYZ";
let mut s_clone = s_string.clone();
s_clone.push_str(s_str);
assert_eq!(s_clone, "abcXYZ");
assert_eq!(s_string, "abc");
assert_eq!(s_str, "XYZ");
以降で説明するpushや、内部でpush_strを使用する+=, +演算子でも同様。元のStringを残したい場合はcloneを使う。上述のformat!マクロを使ってもよい。
push
Stringに文字列ではなく文字charを連結したい場合はpushメソッドを使う。
let mut s_string = String::from("abc");
let c = '!';
s_string.push(c);
assert_eq!(s_string, "abc!");
assert_eq!(c, '!');
シングルクォーテーション'で囲むと文字charになるが、同じ1文字でもダブルクォーテーション"で囲むと文字列&strになる。その場合はpushではなくpush_strを使う。
let mut s_string = String::from("abc");
let s_str = "!";
s_string.push_str(s_str);
assert_eq!(s_string, "abc!");
assert_eq!(s_str, "!");
+=演算子
Stringに対する+=演算子はpush_strメソッドと等価。
impl AddAssign<&str> for String {
#[inline]
fn add_assign(&mut self, other: &str) {
self.push_str(other);
}
}
a += bはa.push_str(b)と等価。左オペランドaはミュータブルなStringで右オペランドbは&str(またはStringの参照)でなければならない。
let mut s_string = String::from("abc");
let s_str = "XYZ";
s_string += s_str;
assert_eq!(s_string, "abcXYZ");
assert_eq!(s_str, "XYZ");
let mut s_string1 = String::from("abc");
let s_string2 = String::from("XYZ");
s_string1 += &s_string2;
assert_eq!(s_string1, "abcXYZ");
assert_eq!(s_string2, "XYZ");
+演算子
Stringに対する+演算子でも、内部でpush_strメソッドが使われている。
impl Add<&str> for String {
type Output = String;
#[inline]
fn add(mut self, other: &str) -> String {
self.push_str(other);
self
}
}
a + bはa.push_str(b)としてからaを返す。左オペランドaはミュータブルなStringで右オペランドbは&str(またはStringの参照)でなければならない。
左オペランドの変数の所有権は移動し失われるので注意。
let s_string = String::from("abc");
let s_str = "XYZ";
let s = s_string + s_str;
assert_eq!(s, "abcXYZ");
assert_eq!(s_str, "XYZ");
// assert_eq!(s_string, "abc");
// error[E0382]: borrow of moved value: `s_string`
let s_string1 = String::from("abc");
let s_string2 = String::from("XYZ");
let s = s_string1 + &s_string2;
assert_eq!(s, "abcXYZ");
assert_eq!(s_string2, "XYZ");
// assert_eq!(s_string1, "abc");
// error[E0382]: borrow of moved value: `s_string1`
空のStringに追加
空のStringを生成して、push_strや+=演算子などで別の文字列を追加していくこともできる。
空のStringはString::newまたはString::with_capacityで生成可能。
let s_string = String::from("abc");
let s_str = "XYZ";
let mut s = String::new();
s.push_str(&s_string);
s.push_str(s_str);
assert_eq!(s, "abcXYZ");
let mut s = String::with_capacity(100);
s.push_str(&s_string);
s.push_str(s_str);
assert_eq!(s, "abcXYZ");
String::with_capacityは容量capacityを確保して空のStringを生成する。最終的なデータ量の目安が分かっている場合は、String::with_capacityを使うと余計な再割り当てを避けることができる。
Stringにおけるcapacityは文字数ではなくバイト数単位なので注意。capacityについては以下の記事を参照。
&strにString, &strを連結したい場合
push_str, pushはStringのメソッドなので、&strからは使えない。また、&strを左オペランドとする+, +=演算子は定義されておらず使えない。
文字列スライス&strを先頭として文字列を連結したい場合、先頭の&strをStringに変換する必要がある。
&strはString::fromやto_stringなどでStringに変換できる。
let s_str = "abc";
let s_string = String::from("XYZ");
let mut s_str_string = String::from(s_str);
s_str_string.push_str(&s_string);
assert_eq!(s_str_string, "abcXYZ");
let s = String::from(s_str) + &s_string;
assert_eq!(s, "abcXYZ");
空のStringを生成してから追加していく場合や、format!を使う場合は、&strが先頭でも問題ない。
let s_str = "abc";
let s_string = String::from("XYZ");
let mut s = String::new();
s.push_str(s_str);
s.push_str(&s_string);
assert_eq!(s, "abcXYZ");
let s = format!("{}{}", s_str, s_string);
assert_eq!(s, "abcXYZ");