(自分が前書いた 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 ↩