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<Target = str>
を実装しており、参照外し時における「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>
から &[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<str> - 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<RangeFull> - 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<str> - String in std::string - Rust」
ちなみに Rust で文字列をそのままをインデックスで操作すると、Unicode の文字単位ではなく UTF-8 のバイト単位の操作になるため、注意が必要です。
2. 実際にはどの方法を使うか?
実質行っている処理はどれも同じですが、可読性を考えると &s
で型指定するか s.as_str()
が良いと思います。
※ &s
に関しては戻り値の型指定必須。
&s
と異なり、全範囲インデックスの指定によって明示的に &str
型に変換する &s[..]
を使用することも考えられます。
変数束縛せずに値を比較したりする場合には &s[..]
が便利かもしれません。