rs.nkmk.me

Rustで&strとStringを相互に変換

Posted: | Tags: Rust, 文字列

Rustで文字列スライス&strと文字列Stringを相互に変換する方法について説明する。

説明のため、変数の型の名前を返す以下の関数を用いる。

fn type_of<T>(_: &T) -> &'static str {
    std::any::type_name::<T>()
}

&strをStringに変換

&strStringに変換(&strからStringを生成)するには以下の方法がある。

  • to_owned
  • to_string
  • String::from
  • into

Rust1.9(2016年5月)まではto_stringが遅いという違いがあったが、1.9以降はすべて同じ性能。

As of Rust 1.9, str::to_string, str::to_owned, String::from, str::into all 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

どれを使うかは好みの問題ではあるが、&strStringの違いは所有権を持つかどうかであり、to_ownedが処理の意図をより明確に表しているという意見がある。

とはいえ、初心者にとってはto_ownedよりto_stringString::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)
    }
}
source: string.rs

String::from

String::fromFromトレイトで実装されている。

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()
    }
}
source: string.rs

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の関数に対しては&StringStringの参照)を渡せる。明示的に&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");

関連カテゴリー

関連記事