LoginSignup
4
2

More than 1 year has passed since last update.

[Rust] &str と &&str で呼ばれる to_string メソッドが異なる

Last updated at Posted at 2021-09-30

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
case_1 の LLVM IR
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"
case_2 の LLVM IR
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)
    }
}

参考「Display - str - Rust

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2