Rust で文字列 String を文字列スライス str に変換するには、借用する (参照を得る) か、または as_str メソッドを使用します。

let s: String = String::from("str");

let ss: &str = &s;
// or
let ss: &str = s.as_str();

※説明のために変数全てで型指定をしていますが、String::from メソッドや as_str メソッドは戻り値の型指定を省略できます。

他にも変換手段がありますが、それらを含めて文字列 String から文字列スライス str に変換される仕組みについてまとめます。

文字列スライス str から文字列 String への変換については別記事にしました。

参考「[Rust] 文字列スライス str から文字列 String への変換とその仕組み – Qiita」

1. 文字列 String を文字列スライス str に変換する 8 つの方法

本記事では以下のコードのような 8 つの変換方法について説明します。

※本質的には 3 つの変換 (※後述) の組み合わせになります。

let s: String = String::from("str");

let ss: &str = s.as_str();
// or
let ss: &str = &s;
// or
use std::ops::Deref;
let ss: &str = s.deref();
// or
let ss: &str = s.as_ref();
// or
let ss: &str = &s[..];
// or
use std::ops::Index;
let ss: &str = s.index(..);
// or
use std::borrow::Borrow;
let ss: &str = s.borrow();
// or
use std::str;
let ss: &str = str::from_utf8(s.as_bytes()).unwrap();

1.1. UTF-8 バイト列から文字列スライス str に変換

String 型から変換する前に、まずは UTF-8 バイト列からの変換を確認します。

use std::str;

let s: String = String::from("str");

let bytes: &[u8] = s.as_bytes();

let ss: &str = str::from_utf8(bytes).unwrap(); // &[u8] -> &str

参考「from_utf8 in std::str – Rust」

1.2. Deref 型強制による変換

String 型は Deref を実装しており、参照外し時における「Deref 型強制」によって &String 型から &str 型に型強制されます。

let s: String = String::from("str");

let ss: &str = &s;
// or
use std::ops::Deref;
let ss: &str = s.deref();

String 型における Deref トレイトの実装は以下のようになっています。

pub struct String {
    vec: Vec<u8>,
}
impl ops::Deref for String {
    type Target = str;

    #[inline]
    fn deref(&self) -> &str {
        unsafe { str::from_utf8_unchecked(&self.vec) }
    }
}

参考「Deref – String in std::string – Rust」

from_utf8_unchecked 関数は from_utf8 関数の安全でない版で、正しい UTF-8 バイト列かどうかを確認せずに文字列スライス str に変換します。

from_utf8_unchecked 関数の引数において &Vec から &[u8] に型強制されます。

参考「from_utf8_unchecked in std::str – Rust」

as_str メソッドや as_ref メソッドは、中で Deref 型強制を行っているだけです。

impl String {
    // ...
    pub fn as_str(&self) -> &str {
        self
    }
    // ...
}
impl AsRef<str> for String {
    #[inline]
    fn as_ref(&self) -> &str {
        self
    }
}

参考「as_str – String in std::string – Rust」
参考「AsRef – String in std::string – Rust」

1.3. インデックス操作による変換

String 型ではインデックス操作を可能にするために Index トレイトを実装しています。

また、インデックス値に範囲境界を使用できるようになっていて、範囲 .. すなわち RangeFull に関して以下のように実装しています。

impl ops::Index<ops::RangeFull> for String {
    type Output = str;

    #[inline]
    fn index(&self, _index: ops::RangeFull) -> &str {
        unsafe { str::from_utf8_unchecked(&self.vec) }
    }
}

処理内容は String 型における Deref トレイトの実装と同じです。

参考「Index – String in std::string – Rust」
参考「RangeBounds in std::ops – Rust」(「範囲境界」)
参考「Index in std::ops – Rust」

よって、全範囲インデックスによって以下のように文字列 String から文字列スライス str に変換できます。

let s: String = String::from("str");

let ss: &str = &s[..];
// or
use std::ops::Index;
let ss: &str = s.index(..);

borrow メソッドは、中でインデックス操作を行っているだけです。

impl Borrow<str> for String {
    #[inline]
    fn borrow(&self) -> &str {
        &self[..]
    }
}

参考「Borrow – String in std::string – Rust」

ちなみに Rust で文字列をそのままをインデックスで操作すると、Unicode の文字単位ではなく UTF-8 のバイト単位の操作になるため、注意が必要です。

2. 実際にはどの方法を使うか?

実質行っている処理はどれも同じですが、可読性を考えると &s で型指定するか s.as_str() が良いと思います。

※ &s に関しては戻り値の型指定必須。

&s と異なり、全範囲インデックスの指定によって明示的に &str 型に変換する &s[..] を使用することも考えられます。
変数束縛せずに値を比較したりする場合には &s[..] が便利かもしれません。

广告
将在 10 秒后关闭
bannerAds