Rustでは文字列を扱う型が2種類あります。
ざっくりと「可変な文字列」と「不変な文字列」の2つになります。
今回はそんなRustにおける文字列について見ていきたいと思います。
今までと同様、自身の勉強のアウトプット・整理と同時に、同じようにRustを勉強している方や「公式ドキュメントはちょっと、、」というような方が気楽に見れるようにサンプルコードをメインに簡単にまとめてみました。
- String, &strの基本
- String, &strの便利なメソッドとその使い方
基本
Rustにおける文字列は以下の2種類があります。
String
&str
使い方は以下のようになります。
fn sample() {
let string_from = String::from("string_from");
let string_to = "string_to".to_string();
println!("{}", string_from); // -> string_from
println!("{}", string_to); // -> string_to
let str = "str";
println!("{}", str); // -> str
}
2つの文字列型の違い
2つの文字列の違いは大きく「可変」か「不変」かというところです。
もう少し詳しく見てみると以下のような違いがあります。
&str
-
- 文字列への参照(基本的には不変)
- 文字列スライスとも呼ばれる
- プリミティブ型
- 文字列リテラルは静的な値となる
サンプルコード
fn sample() { let string: String = String::from("string"); // 上記Stringのスライス(参照) let str: &str = &string; // 文字列リテラルは静的なライフタイム=静的領域に配置された文字列のスライス let static_str: &'static str = "static"; }
-
参考リンクstd::str – Rust Utilities for the `str` primitive type.
String
-
- 標準ライブラリで定義されている型
- 他言語における文字列クラスのようなもの
mut
をつけることでミュータブル(変更可能)な文字列となる
サンプルコード
fn sample() { let string: String = String::from("string"); // string.push('!'); // mutableではないので変数自身を変更する操作はできない let mut mut_string: String = String::from("mutable string"); mut_string.push('!'); // mutableの場合は変数自身を変更する操作も可能 }
参考リンクString in std::string – Rust A UTF-8–encoded, growable string.
四則演算
文字列においては多言語と同様に加算による連結が可能。
fn sample() {
let string1: String = String::from("string1");
let string2: String = String::from("string2");
let str1: &str = "str1";
let str2: &str = "str2";
println!("{}", string1 + str2);
println!("{}", string1); // 加算を行った変数は所有権を失うのでエラーとなる
println!("{}", string1 + string2); // 右辺は&strでないとエラー
println!("{}", str1 + str2); // &strに対しては加算できない
println!("{}", str1 + string2) // &strに対しては加算できない
}
便利なメソッド(共通)
&str
,String
のいずれにも使用できるメソッドをいくつかピックアップします。
len
文字列のバイト数を取得する。
fn sample() {
let string: String = String::from("テスト");
println!("{}", string.len()); // -> 9
let str: &str = "テスト2";
println!("{}", str.len()); // -> 10
}
文字数を取得したい場合は下記のようにchars
を使用する。
fn sample() {
let string: String = String::from("テスト");
println!("{}", string.chars().count()); // -> 3
let str: &str = "テスト2";
println!("{}", str.chars().count()); // -> 4
}
is_empty
文字列が空白かどうかを判定する。
fn sample() {
let string1: String = String::from("テスト");
println!("{}", string1.is_empty()); // -> false
let string2: String = String::from("");
println!("{}", string2.is_empty()); // -> true
let str1: &str = "テスト2";
println!("{}", str1.is_empty()); // -> false
let str2: &str = "";
println!("{}", str2.is_empty()); // -> true
}
lines
文字列を改行区切りのiterator
として取得する。
対応している文字コードは\n
,\r\n
の2種類。\r
には対応していない。
fn sample() {
let string1: String = String::from("タイトル\n内容\n内容2");
for line in string1.lines() {
println!("{}", line);
// タイトル
// 内容
// 内容2
}
let str1: &str = "line1\nline2\r\nline3\rline4";
for line in str1.lines() {
println!("{}", line);
// line1
// line2
// line4
}
}
contains
対象の文字列に引数として指定した文字列が存在するか判定する。
fn sample() {
let string: String = String::from("start dummy end");
println!("{}", string.contains("dummy")); // -> true
println!("{}", string.contains("rust")); // -> false
let str: &str = "start dummy end";
println!("{}", str.contains("dummy")); // -> true
println!("{}", str.contains("rust")); // -> false
}
start_with
対象の文字列が引数として指定した文字列で始まるかを判定する。
fn sample() {
let string: String = String::from("start dummy end");
println!("{}", string.starts_with("start")); // -> true
println!("{}", string.starts_with("rust")); // -> false
let str: &str = "start dummy end";
println!("{}", str.starts_with("start")); // -> true
println!("{}", str.starts_with("rust")); // -> false
}
end_with
対象の文字列が引数として指定した文字列で始まるか/終わるかを判定する。
fn sample() {
let string: String = String::from("start dummy end");
println!("{}", string.ends_with("end")); // -> true
println!("{}", string.ends_with("rust")); // -> false
let str: &str = "start dummy end";
println!("{}", str.ends_with("end")); // -> true
println!("{}", str.ends_with("rust")); // -> false
}
split系
- split
-
指定した文字で文字列を分割する。
fn sample() { let string: String = String::from("a,b,c,d,e"); println!("{:?}", string.split(',').collect::<Vec<&str>>()); // -> ["a", "b", "c", "d", "e"] let str: &str = "a,b,c,d,e"; println!("{:?}", str.split(',').collect::<Vec<&str>>()); // -> ["a", "b", "c", "d", "e"] }
- split_whitespace
-
文字列を空白区切りで分割する。
全角スペースも区切り対象となる。
fn sample() { let string: String = String::from("a b\tc\r\nd e"); println!("{:?}", string.split_whitespace().collect::<Vec<&str>>()); // -> ["a", "b", "c", "d", "e"] let str: &str = "a b\tc\r\nd e"; println!("{:?}", str.split_whitespace().collect::<Vec<&str>>()); // -> ["a", "b", "c", "d", "e"] }
- split_ascii_whitespace
-
文字列を空白区切りで分割する。
こちらは全角スペースは区切り対象とならないことに注意。
fn sample() { let string: String = String::from("a b\tc\r\nd e"); println!( "{:?}", string.split_ascii_whitespace().collect::<Vec<&str>>() ); // -> ["a", "b", "c", "d\u{3000}e"] let str: &str = "a b\tc\r\nd e"; println!("{:?}", str.split_ascii_whitespace().collect::<Vec<&str>>()); // -> ["a", "b", "c", "d\u{3000}e"] }
- split_terminator
-
指定した文字で文字列を分割する。
splitとの違いは分割後の末尾が空の場合は無視されること。
fn sample() { let string: String = String::from("a,b,c,"); println!("{:?}", string.split(',').collect::<Vec<&str>>()); // -> ["a", "b", "c", ""] println!("{:?}", string.split_terminator(',').collect::<Vec<&str>>()); // -> ["a", "b", "c"] let str: &str = "a,b,c,"; println!("{:?}", str.split(',').collect::<Vec<&str>>()); // -> ["a", "b", "c", ""] println!("{:?}", str.split_terminator(',').collect::<Vec<&str>>()); // -> ["a", "b", "c"] }
- splitn
-
指定した文字で文字列を分割する。
splitとの違いは、最大
n
個に分割すること。fn sample() { let string: String = String::from("a,b,c,d,e"); println!("{:?}", string.splitn(3, ',').collect::<Vec<&str>>()); // -> ["a", "b", "c,d,e"] let str: &str = "a,b,c,d,e"; println!("{:?}", str.splitn(3, ',').collect::<Vec<&str>>()); // -> ["a", "b", "c,d,e"] }
mathces
指定した文字にマッチした文字列を抽出する。
char::is***
を利用すると簡単にパターンマッチできるので便利。
fn sample() {
let string: String = String::from("a,b,c,1,2d,e");
println!("{:?}", string.matches("2d").collect::<Vec<&str>>());
// -> ["2d"]
println!("{:?}", string.matches(char::is_alphabetic).collect::<Vec<&str>>());
// -> ["a", "b", "c", "d", "e"]
let str: &str = "a,b,c,1,2d,e";
println!("{:?}", str.matches("a").collect::<Vec<&str>>());
// -> ["a"]
println!("{:?}", str.matches(char::is_numeric).collect::<Vec<&str>>());
// -> ["1", "2"]
}
replace
指定文字にマッチした文字列を置換する。
mathces
と同様にchar::is***
を利用できる。
fn sample() {
let string: String = String::from("a,b,c,1,2d,e");
println!("{:?}", string.replace("a", "dummy"));
// -> "dummy,b,c,1,2d,e"
println!("{:?}", string.replace(char::is_alphabetic, "dummy"));
// -> "dummy,dummy,dummy,1,2dummy,dummy"
let str: &str = "a,b,c,1,2d,e";
println!("{:?}", str.replace("a", "dummy"));
// -> "dummy,b,c,1,2d,e"
println!("{:?}", str.replace(char::is_numeric, "dummy"));
// -> "a,b,c,dummy,dummyd,e"
}
trim
先頭と末尾の空白を取り除いた文字列を取得する。
fn sample() {
let string: String = String::from(" abc\t");
println!("{:?}", string.trim());
// -> "abc"
let str: &str = "\r\nabc ";
println!("{:?}", str.trim());
// -> "abc"
}
parse
文字列を別の型にパースする。
パースできた場合はOk(T)
、パースに失敗した場合はErr(E)
を返す。
fn sample() {
let string: String = String::from("100");
println!("{:?}", string.parse::<u32>());
// -> Ok(100)
let str: &str = "abc";
println!("{:?}", str.parse::<u32>());
// -> Err(ParseIntError { kind: InvalidDigit })
}
uppercase / lowercase
文字列を大文字/小文字に変換する。
ascii文字のみ対象とする場合はto_ascii_uppercase
,to_ascii_lowercase
を使用する。
ascii文字以外の文字(例えばウムラウト)も対象としたい場合はto_uppercase
,to_lowercase
を使用する。
fn sample() {
let string: String = String::from("aScIi ウムラウトäÖü");
println!("{:?}", string.to_ascii_uppercase()); // -> "ASCII ウムラウトäÖü"
println!("{:?}", string.to_ascii_lowercase()); // -> "ascii ウムラウトäÖü"
let str: &str = "aScIi ウムラウトäÖü";
println!("{:?}", str.to_lowercase()); // -> "ascii ウムラウトäöü"
println!("{:?}", str.to_uppercase()); // -> "ASCII ウムラウトÄÖÜ"
}
repeat
同じ文字を複数回繰り返した文字列を返す。
fn sample() {
let string: String = String::from("abc");
println!("{:?}", string.repeat(2));
// -> "abcabc"
let str: &str = "DEF";
println!("{:?}", str.repeat(5));
// -> "DEFDEFDEFDEFDEF"
}
便利なメソッド(String)
次に示すメソッドはString
向けのメソッドです。
対象となる変数がmut
であることに注意してください。
push
1文字(char
)をString
の末尾に追加する。
fn sample() {
let mut string: String = String::from("a");
println!("{:?}", string);
// -> "a"
string.push('b');
println!("{:?}", string);
// -> "ab"
}
push_str
文字列スライス(&str
)をString
の末尾に追加する。
fn sample() {
let mut string: String = String::from("a");
println!("{:?}", string);
// -> "a"
string.push_str("cde");
println!("{:?}", string);
// -> "acde"
}
clear
String
の中身を空にする。
fn sample() {
let mut string: String = String::from("abcde");
println!("{:?}", string);
// -> "abcde"
string.clear();
println!("{:?}", string);
// -> ""
}
マクロ
format!
複数文字の連結やテンプレート文字列に変数を埋め込みたい場合にはformat!
マクロを使用すると便利。
JavaScriptをやったことがある方はテンプレートリテラルと同じ認識で基本的にはOK。
fn sample() {
let name: String = String::from("Rust 太郎");
let welcome: String = format!("ようこそ {}!!", name);
println!("{}", welcome);
// -> ようこそ Rust 太郎!!
let id = 123;
let message: String = format!("ID: {}", id);
println!("{}", message);
// -> "ID: 123"
}
最後に
文字列スライスという概念がRustならではのものです。
ただ単純に文字を扱う上ではそこまで気にする必要のないものですが、スライスについて理解を深めるとより良い方法で文字列を扱うことができます。
せっかくのRustプログラミングですので、細かい部分の理解を深めて安全安心なプログラミングができるように勉強していこうと思います。