Rust における String -> &str 操作の違い
とりあえずの結論
今回あげる 5 つの方式に全くの違いはない
書き方が異なるぐらいで処理内容も同じ (なはず)
前提知識
String そのものと (一部) ソースコード
String から &str へ変換する方法
Borrow と AsRef トレイト
--- 以下は任意 ---
Borrow と ToOwned トレイト
String と &str の変換がどのようなものかという話
String を構造体に持たせたいとき
おまけで使う予定
本編
Rust において String から &str に変換にする方法は複数個ある 逆に &str から String へ変換する方法も複数個ある
今回あげる変換方法は以下の 5 つ
as_str メソッド
borrow メソッド
Borrow トレイトの実装
as_ref メソッド
AsRef トレイトの実装
参照 + スライス
参照のみ
実験環境
Rust version: 1.69.0
実験ソースコード
code:main.rs
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
// ref only
assert_eq!(&s5, s);
}
(16 行目と 20 行目については後で解説する)
結果
コンパイル (cargo build) できたか?
=> できた
実行時 (cargo run) に panic が起きなかったか?
正常終了したか?と同等な意味
=> 起きなかった (正常終了した)
解説
as_str メソッド
ソースコードから見てわかるようにこのメソッド &self をそのまま返している
これは String の参照が &str と同値となっているからである
Deref トレイトを実装しているので
そのため String 構造体における &self の型は &str となる
また, このメソッドは Rust が 1.0.0 のときは存在せず, 1.7.0 から使えるようになった
経緯はあとでかくかも
borrow メソッド (Borrow トレイト実装) と as_ref メソッド (AsRef トレイト実装)
中身は大体同じなんでまとめた
この二つの肝はトレイトという仕組みでちょっとコツがいる
borrow メソッド
as_ref メソッド
まず, 両方のソースコードの違いを見る
どちらとも &str を返している
borrow メソッドでは処理は &self[..] で返している
後述する参照 + スライスと同じ
as_ref メソッドでは as_str と全く同じである
どちらとも同じように見えるが (メソッドの意味としての) 違いは一応ある
詳細は前提知識に書いてある Borrow と AsRef トレイトそのものってだけ
今回の場合は結果的に同じ処理になっている
次に実験ソースコードを見る
code:main.rs
// 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);
どちらも一時的に &str と型明記された変数を使って assert_eq! をしている
コメントアウトしてる行のように as &str で型変換してもいける
が, 本来の用途的にはあんまり推奨はできないと思う…?
実はこうしないとコンパイルが通らない
どうしてかと言うと, これらのトレイトには実装が複数個あるため, そのままだと型推論ができない
だから型指定が必要だったんですね
なので, この二つのメソッドをまとめたのは, このトレイトの仕様を知る必要があるというのを強調したかった
参照 + スライス
String の参照は &str である
&str はスライスである
よって, String の参照 + スライスはスライス (&str) を返す (Q.E.D)
参照
String の参照は &str であるので
厳密には &String から &str への暗黙の型変換みたいなのは行われているけど