【Rust入門】文字列の使い方(strとString)

Rustの文法で難しく感じるものの一つに文字列があります。
難しい理由はいくつかあるのですが、文字列を表すのに2つの型があることがその原因の一つです。

今回は文字列についてです。

※少し長めの内容です。
細かいことは知らなくて良いというなら、最後のまとめだけご覧ください。

Rustで文字列を表す型とは?

Rustは文字列を表す型としてstr型とString型があります。

まずはそれぞれの使い方をご紹介します。

fn main() {
	let s1 : &str = "this is str";
	let s2 : String = String::from("this is String");

	println!("{}", s1);
	println!("{}", s2);
}

s1がstr型、s2がString型です。
どちらも同じように文字を出力することができます。
イメージとしてはstr型は直接文字列を代入できる。
String型は文字列の初期化が何やら面倒そう…という感じです。
ではそれぞれの違いをご紹介します。

strとStringの違いは?

str型とString型、違いは色々ありますが、簡単に言うと、strは変更できない文字列、Stringは変更できる文字列といういとです。

strとはどんな型なのか?

まずはstrについてです。
strはプリミティブ型の一種です。
プリミティブ型を忘れたならこちら。
【Rust入門】基本データ型(プリミティブ型)について
簡単に言うと、i8とかi32みたいによく使う一般的な型ということです。strもこれの一種になります。

そして、変数を明示的にstr型として指定する時は「&」を付けて宣言します。
以下が例です。

fn main() {
	let text : &str = "hello";

	println!("{}", text);
}

2行目の型指定で「&str」として宣言しています。
この「&」は参照型という意味になります。

なぜstrの型指定は「&」がいるの?

strの前に「&」が必要なのは、文字列”hello”の参照を受けているからです。

上の例では、2行目の”hello”という文字列はリテラルになります。
リテラルというのはプログラム上に直接書かれた文字や数字のことです。

この文字列リテラルは「&’static str」という型になります。
これは、メモリのどこかに”hello”という文字分のメモリを確保し、そのメモリの位置を参照するということです。

そして、参照を受けるというのは、メモリ上の位置(アドレス)を教えてもらうということになります。

つまり、例文の変数textは、”hello”という文字が存在するメモリ上の位置を指しているだけということです。
さらにそのメモリは”hello”という文字が入る大きさしか割り当てられていません。
そのため変更できないということですね。

ここまでで、なぜ「&」が必要なのかということですが、textは文字列”hello”の参照になります。参照をするには、変数の型も参照型にしないといけません。
そして、参照型にするには、変数の型の前に「&」を付ける必要があります。
そのため、strは「&」が必要ということです。

文字列リテラルの型は?

ちなみに、文字列リテラルは&’static str型なので、

fn main() {
	let text : &'static str = "rust";

	println!("{}", text);
}

このように&strではなく、&’static strとしても文字列を出力することができます。

Stringとはどんな型なのか?

次にStringについてです。
Stringはstrと違い、プリミティブ型ではありません。

そして、strとは違い、文字列を追加したり変更したりすることができます。

fn main() {
	let mut s : String = String::from("hello");

	s += " world";

	println!("{}", s);
}

上は文字列”hello”に” world”を追加して出力する例です。
まずString::fromを使ってString型の文字列を作成します。

ここで大事なのは、let mut として変数を作成することです。
この「mut」は変更可能という意味になります。
詳しくは次でお話します。

このようにして変更可能な変数にすると += を使って&str型の文字列を追加することができます。

こちらの例も見てください。

fn main() {
	let mut s1 : String = String::from("hello");
	let mut s2 : String = String::from(" world");

	s1 += s2;

	println!("{}", s1);
}

先ほどと同じに感じますが、こちらはコンパイルエラーになります。
String型を加えることはできません。
この場合、加えるときにs2に「&」をつけて参照を渡すようにしないといけません。

fn main() {
	let mut s1 : String = String::from("hello");
	let mut s2 : String = String::from(" world");

	s1 += &s2;

	println!("{}", s1);
}

こうすると無事に文字列を加えることができます。

文字列を簡単に足す方法

これだけ見ても分かるように文字列の扱いはかなり複雑です…
最初はなかなかコンパイルが通らなくてかなり苦戦すると思います。
そこで良い方法をご紹介します。
それは、format!を使う方法です。

fn main() {
	let mut s1 : String = String::from("hello");
	let mut s2 : String = String::from(" world");

	s1 = format!("{}{}", s1, s2);

	println!("{}", s1);
}

このようにformat!を使えば、簡単に合成することができます。さらにformat!は&str型でもString型でも関係なく合成することができます。
format!の戻り値はString型になります。

文字列から一文字取る

Rustでは、文字はchar型の変数にいれます。
char型は4バイトの変数です。
C言語だと1バイトですが、Rustでは様々な言語や絵文字に対応するために4バイトになっています。

文字列から一文字を取得するには以下のようにします。

fn main() {
	let mut text : String = String::from("hello");
	let v : Vec::<char> = text.chars().collect();

	println!("{}", v[0]);
}

一文字取るにもここまで複雑に書かなくてはいけません。
おまけに方法は一つではなく他にもたくさんあります。
最初のうちは意味を考えず定型文として覚えてしまうのも一つの手だと思います。

Rustの文字列のまとめ

  • 文字列はstrとString型がある。
  • strは変更できない文字列、Stringは変更できる文字列になる。
  • 文字列の結合は難しいのでformat!を使うのがオススメ。これだと意外と簡単にできる。

色々と話しましたが、とりあえずこれだけは覚えておいてください。

スポンサーリンク