Hello, I am somewhat new to the language. I came across some behavior that is a little bit confusing. I thought .as_deref() on an Option<&String> would return an Option<&str> through Deref coercion, but it is isn't. I tried looking at the documentation for both the .as_deref() method and the impl Deref for String part, and I can't figure out why it isn't returning an Option<&str>
#![allow(unused)]
fn main() {
let p = String::from("hey");
let x = Some(&p);
assert_eq!(x.as_deref(), Some("hey"));
}
Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
--> src/main.rs:5:30
|
5 | assert_eq!(x.as_deref(), Some("hey"));
| ^^^^^^^^^^^ expected `Option<&String>`, found `Option<&str>`
|
= note: expected enum `Option<&String>`
found enum `Option<&str>`
For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground` (bin "playground") due to 1 previous error
I thought maybe this could be because the underlying .deref() could work differently on a &String than it does a String, but this shows otherwise:
let test = String::from("hello");
let op = Some(&test);
println!("{}", type_name_of_val(&op));
let wow = match op {
Some(t) => Some(t.deref()),
None => None,
};
println!("{}", type_name_of_val(&wow));
output:
:!cargo run
[No write since last change]
Compiling chapter v0.1.0 (/home/duc/Documents/Projects/learning_rust/chapter-15/chapter)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.13s
Running `target/debug/chapter`
core::option::Option<&alloc::string::String>
core::option::Option<&str>
It's because references implement Deref to themselves. The .as_deref() method lets you go from Option<String> to Option<&str>, but it doesn't help you for Option<&String>.
I prefer x.map(|s| s.as_str()), or x.map(String::as_str)[1]
fun fact:
if you use x.map(<String as Deref>::deref) (or the shorthand x.map(String::deref), or even x.map(Deref::deref)), it actually works too.
if you search functions with the type signature &String->&str in the standard library, you get 4 functions with the exact signature, among which only String::as_str() is an inherent (and const) method on String, the others are all trait methods.
it's slightly longer, but when reading code, double * operators always makes me stop and think longer than I wanted âŠī¸