(自分が前書いた Scrapbox の内容 を軽くまとめたものなので中身はそこまで変わらないです)
(なんなら Scrapbox の方が詳しい可能性もあります)
最終的な結論
どれでも同じだが、.as_str() 使えば良いです。
前置き
Rust において String から &str に、またはその逆に変換したいことはあると思います。
が、やり方は複数個あるため違いがわかりにくいかと思います (実際、ドキュメントの記載にないものもあるので余計混乱しやすいかもしれません、自分はそうでした)。
なので、今回は String から &str に変換するやり方を 5 つ取り上げてそれぞれ Rust 本体のソースコードも踏まえて説明します。
環境
- Rust: 1.69.0
-
Rust Playground で実行
- 実際に使ったソースコードはこちら
紹介する変換方法
as_str()-
Borrowトレイトを用いた変換 -
AsRefトレイトを用いた変換 - 参照
- 参照 + スライス
実験ソースコード
use std::borrow::Borrow;
fn main() {
let s = "foo";
let s1 = s.to_string();
let s2 = s.to_string();
let s3 = s.to_string();
let s4 = s.to_string();
let s5 = s.to_string();
// as_str()
assert_eq!(s1.as_str(), s);
// borrow()
let string_borrow: &str = s2.borrow();
assert_eq!(string_borrow, s);
//assert_eq!(s2.borrow() as &str, s);
// as_ref()
let string_as_ref: &str = s3.as_ref();
assert_eq!(string_as_ref, s);
//assert_eq!(s3.as_ref() as &str, s);
// slice
assert_eq!(&s4[..], s);
// ref only
assert_eq!(&s5, s);
}
解説
as_str()
assert_eq!(s1.as_str(), s);
1.7.0 で追加された String 専用のメソッド。
実装は、&self をそのまま返している1。
&self で &str を返せるのは String が Deref トレイトを実装しているため23。
Borrow トレイトを用いた変換
let string_borrow: &str = s2.borrow();
assert_eq!(string_borrow, s);
Borrow トレイトで実装されている borrow() メソッドで変換。
実装は &self[..] を返している4。
Borrow トレイトは prelude されていない (最初から使えるわけではない) ので、
use std::borrow::Borrow;
で使えるようにする必要がある。
また、後述する AsRef トレイトでもそうだが、String には複数の Borrow トレイト実装があるため、型を明記しないとコンパイルエラーを吐く。
AsRef トレイトを用いた変換
let string_as_ref: &str = s3.as_ref();
assert_eq!(string_as_ref, s);
AsRef トレイトで実装されている as_ref() メソッドで変換。
実装は as_str() メソッドと全く同じ5。
こちらは prelude されているので use を使わなくても使用できる。
また、Borrow トレイトと同様に型を明記しないとコンパイルエラーを吐く。
参照
// ref only
assert_eq!(&s5, s);
as_str() で説明したように、String が Deref トレイトを実装しているため参照で &str を返せる。
注意点として、&String と型推論される場合があるので、これを使用する場合は型明記した方がいいかもしれない。
参照 + スライス
// slice
assert_eq!(&s4[..], s);
参照でほぼ解決してる感じはあるけど、&str 自体はスライスなのでこのように参照とスライスで表記できる。
結果と結論
どの方法も実装は多少異なれど、同じ内容なので、as_str() を使うのが一番わかりやすい (実際メソッド名で一発で変換していることがわかるので)。
余談
String の実態は Vec<u8>6 なのでもしかしたら参照と参照 + スライスで実際は構造が違う可能性はあるかもしれません…。
Vec<T> が cap, len 等の中身を持つ構造体なので、その値がその二つで異なる可能性はあるかと思います (実際に実験はしてないので検証は必要ですが…)。
参考文献
-
https://doc.rust-lang.org/src/alloc/string.rs.html#2442-2449 ↩
-
トレイトおよび Deref トレイトは今回は説明を省略していただきます…一応、The Rust Programming Language に説明はあるのでそれ確認していただければ ↩
-
https://doc.rust-lang.org/src/alloc/string.rs.html#365-367 ↩