to_string メソッドではレシーバの自動参照外しが行われません (※後述) 。
少なくともバージョン 1.55.0 現在は、&str と &&str で異なる to_string が呼ばれます。
&str のときは String::from と同じ動作をしますが、&&str の場合は違う動作をします。
-
self: &strのとき:<str as ToString>::to_string -
self: &&strのとき:<T as ToString>::to_string
※ self: &&&str 以降の参照も <T as ToString>::to_string
1. LLVM IR で確認
let foo: &&str = &"Foo";
println!("{}", case_1(foo));
println!("{}", case_2(foo));
# [inline(never)]
fn case_1(foo: &&str) -> String {
<str as ToString>::to_string(*foo)
}
# [inline(never)]
fn case_2(foo: &&str) -> String {
<&str as ToString>::to_string(foo)
}
cargo install cargo-asm
cargo llvm-ir test::main::case_1 | grep ToString
cargo llvm-ir test::main::case_2 | grep ToString
br i1 %2, label %bb20.i.i.i.i.i.i.i.i.i.i.i, label %"<str as alloc::string::ToString>::to_string.exit"
"<str as alloc::string::ToString>::to_string.exit"
br i1 %3, label %bb1.i.i, label %"<T as alloc::string::ToString>::to_string.exit"
"<T as alloc::string::ToString>::to_string.exit"
参考「GitHub - gnzlbg/cargo-asm: cargo subcommand showing the assembly or llvm-ir generated for Rust code」
2. ToString トレイトの実装
Display トレイトは参照に対して再帰的にブランケット実装され、さらに Display トレイトを実装する型に対して ToString トレイトがブランケット実装されます。
参考「[Rust] to_string メソッド等は呼び出し時に自動参照外しされない - Qiita」
2.1. &str の場合
バージョン 1.9.0 から、プリミティブ型 str が独自に ToString トレイトを実装し、String::from と同じ動作をするようになっています。
impl ToString for str {
#[inline]
fn to_string(&self) -> String {
String::from(self)
}
}
参考「ToString - str - Rust」(ブランケット実装でないトレイト実装の方)
2.2. &&str 等の場合
少なくともバージョン 1.55.0 現在は &&str 等に対しては独自の実装はされず、Display トレイトを実装する型がブランケット実装する ToString トレイトの to_string メソッドが呼ばれます。
Formatter を介して String のバッファに書き込まれます。
impl<T: fmt::Display + ?Sized> ToString for T {
// ... 略
#[inline]
default fn to_string(&self) -> String {
let mut buf = String::new();
let mut formatter = core::fmt::Formatter::new(&mut buf);
// Bypass format_args!() to avoid write_str with zero-length strs
fmt::Display::fmt(self, &mut formatter)
.expect("a Display implementation returned an error unexpectedly");
buf
}
}
参考「ToString - str - Rust」(ブランケット実装の方)
impl Display for str {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
f.pad(self)
}
}