Rustで&strとStringを相互に変換
Rustで文字列スライス&strと文字列Stringを相互に変換する方法について説明する。
説明のため、変数の型の名前を返す以下の関数を用いる。
- 関連記事: Rustで変数の型の名前を取得・確認
fn type_of<T>(_: &T) -> &'static str {
std::any::type_name::<T>()
}
&strをStringに変換
&strをStringに変換(&strからStringを生成)するには以下の方法がある。
to_ownedto_stringString::frominto
Rust1.9(2016年5月)まではto_stringが遅いという違いがあったが、1.9以降はすべて同じ性能。
As of Rust 1.9,
str::to_string,str::to_owned,String::from,str::intoall have the same performance characteristics. Use whichever you prefer. What is the difference between these 3 ways of declaring a string in Rust? - Stack Overflow
どれを使うかは好みの問題ではあるが、&strとStringの違いは所有権を持つかどうかであり、to_ownedが処理の意図をより明確に表しているという意見がある。
- to_string() vs to_owned() for string literals - help - The Rust Programming Language Forum
- Clippy Lints - str_to_string
とはいえ、初心者にとってはto_ownedよりto_stringやString::fromのほうが意味を理解しやすいので、場合によって好きなものを使えばよいだろう。
to_owned
to_ownedメソッドはToOwnedトレイトで実装されている。
let s_str = "abc";
assert_eq!(type_of(&s_str), "&str");
let s_string = s_str.to_owned();
assert_eq!(type_of(&s_string), "alloc::string::String");
to_string
to_stringメソッドはToStringトレイトで実装されている。
let s_string = s_str.to_string();
assert_eq!(type_of(&s_string), "alloc::string::String");
中身はString::from。
impl ToString for str {
#[inline]
fn to_string(&self) -> String {
String::from(self)
}
}
String::from
String::fromはFromトレイトで実装されている。
let s_string = String::from(s_str);
assert_eq!(type_of(&s_string), "alloc::string::String");
中身はto_owned。
impl From<&str> for String {
/// Converts a `&str` into a [`String`].
///
/// The result is allocated on the heap.
#[inline]
fn from(s: &str) -> String {
s.to_owned()
}
}
into
From<&str> for Stringが実装されているので、対応するIntoトレイトが自動的に実装され、&strからStringを生成するintoメソッドが使える。
String以外の型に対してもFrom<&str> for Tが実装されているので、Stringに変換したい場合はintoの返り値の型がStringだと推論可能でなければならない。letの場合は型注釈が必要。
// let s_string = s_str.into();
// error[E0282]: type annotations needed
let s_string: String = s_str.into();
assert_eq!(type_of(&s_string), "alloc::string::String");
Stringを&strに変換
仮引数&strに対しては&Stringを渡せばよい
仮引数の型が&strの関数に対しては&String(Stringの参照)を渡せる。明示的に&strに変換する必要はない。
fn test_func(s: &str) -> usize {
s.len()
}
let s_string = String::from("abc");
assert_eq!(test_func(&s_string), 3);
これは&Stringが&strに型強制されるから。詳細は以下の記事を参照。
as_str
as_strメソッドでStringを&strに変換できる。
let s_str = s_string.as_str();
assert_eq!(type_of(&s_str), "&str");
スライス
文字列スライス&strは文字通り文字列のスライスなので、[..]で全体のスライスを指定することでStringから&strを得られる。
let s_str = &s_string[..];
assert_eq!(type_of(&s_str), "&str");
参照
上述のように、&Stringは&strに型強制される。
型強制は型が明示された(型注釈が記述された)let文でも起こる。
したがって、&strと型注釈したlet文で&Stringから&strを定義できる。
let s_str: &str = &s_string;
assert_eq!(type_of(&s_str), "&str");
型注釈が無い場合は型強制されず&strではなく&Stringになるので注意。
let s_string_ref = &s_string;
assert_eq!(type_of(&s_string_ref), "&alloc::string::String");