Deref coercions
🤔 Refer to Rust Design Patterns
Do and Don't
- Use
&str
over&String
. - Use
&[T]
over&Vec<T>
. - Use
&T
over&Box<T>
.
Because...
// Do this fn good(foo: &str) { println!("{foo}"); } // Not this fn bad(foo: &String) { println!("{foo}"); } fn main() { // 🤩 Deref coercion happen here. println!("{:?}", good(&"Ferris".to_string())); // &String → &str println!("{:?}", good(&"Ferris")); // &&str → &str println!("{:?}", good("Ferris")); // &str → &str println!("{:?}", bad(&"Ferris".to_string())); // &String → &String // 😱 No coercion here! Uncomment to see an errors. println!("{:?}", bad(&"Ferris")); // expected reference `&String` found reference `&&'static str` println!("{:?}", bad("Ferris")); // expected reference `&String` found reference `&'static str` }
That should show why Deref coercions
is handy, without it we will have to do &&str → &str
ourself and that's no joy. You can enjoy it now or continue reading to dig deeper. 👇
Deref
use std::ops::Deref; #[derive(Debug)] struct Foo<T> { bar: T, } impl<T> Deref for Foo<T> { type Target = T; fn deref(&self) -> &T { &self.bar } } fn main() { let foo = Foo { bar: "bar" }; // Without Deref. println!("{:?}", foo.bar); // With Deref. println!("{:?}", *foo); // Same thing. assert_eq!(*foo, foo.bar); }
Deref auto coercions
fn foo(bar: &str) { println!("{bar}"); } fn main() { // Because String implements Deref<Target=str>. let owned_hello = "Hello".to_string(); // So this work because &String → &str foo(&owned_hello); foo(&&owned_hello); foo(&*&owned_hello); // Why god why? Stop! // Or even this because &Rc<String> → &String → &str foo(&std::rc::Rc::new(owned_hello)); }
Deref method
struct Foo; impl Foo { fn bar(&self) { println!("FooBar"); } } fn main() { // Same thing. Foo.bar(); (&Foo).bar(); (&&Foo).bar(); (&&&Foo).bar(); }