Skip to content

.as_deref() on Option<Box<String>> does not resolve to Option<&str> as expected #139492

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
VinEckSie opened this issue Apr 7, 2025 · 2 comments
Closed
Labels
C-discussion Category: Discussion or questions that doesn't represent real issues.

Comments

@VinEckSie
Copy link

Calling .as_deref() on an Option<Box> does not resolve to Option<&str> as expected.
It seems to resolve to Option<&Box>, leading to a type mismatch (expected &str, found &String) when pattern matching string literals.

This behavior is unexpected, especially given that Box should deref to String, and String to str.

I tried this code:

fn main() {
    let user_mail: Option<Box<String>> = Some(Box::new("[email protected]".to_string()));

    // This should return Option<&str>
    let string_slice = user_mail.as_deref();

    match string_slice {
        Some("[email protected]") => println!("Matched!"),
        Some(other) => println!("Other: {}", other),
        None => println!("None"),
    }
}

I expected to see this happen: .as_deref() on Option<Box> should dereference all the way to Option<&str>, making it matchable with &str literals directly.

Instead, this happened: (mail underlined on this line : Some("[email protected]") => println!("Matched!"))

error[E0308]: mismatched types
  expected `&String`
     found `&str`

Meta

  • ✅ Bug still occurs on stable (1.86.0)
  • ✅ Bug also occurs on beta
  • ✅ Bug also occurs on nightly

rustc --version --verbose:

rustc 1.86.0 (05f9846f8 2025-03-31)
binary: rustc
commit-hash: 05f9846f893b09a1be1fc8560e33fc3c815cfecb
commit-date: 2025-03-31
host: x86_64-apple-darwin
release: 1.86.0
LLVM version: 19.1.7

Backtrace

Not applicable — no panic occurred.


💡 Workaround (Manual Pattern Matching)

While .as_deref() fails to resolve as expected, the following pattern using .as_str() works correctly and behaves as intended:

fn main() {
    let user_mail: Option<Box<String>> = Some(Box::new("[email protected]".to_string()));

    match user_mail {
        Some(ref boxed_string) if boxed_string.as_str() == "[email protected]" => {
            println!("user mail OK!");
        }
        Some(ref other) => {
            println!("user mail KO!: {}", other);
        }
        None => {
            println!("No user email");
        }
    }
}
@VinEckSie VinEckSie added the C-bug Category: This is a bug. label Apr 7, 2025
@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Apr 7, 2025
@zachs18
Copy link
Contributor

zachs18 commented Apr 7, 2025

Option::as_deref does not perform deref coercions, it derefs exactly once.

More specifically, it takes an &Option<T> where T: Deref1 and returns an Option<&<T as Deref>::Target>. Here, you have T = Box<String>, and <Box<String> as Deref>::Target = String, so you get a Option<&String> (Not an Option<&Box<String>>, that's what Option::as_ref would give here).

If you don't want to need to do manual pattern matching, you can also do something like

match user_email.as_ref().map(|ref_to_boxed_string| ref_to_boxed_string.as_str()) {
    Some("[email protected]") => println!("Matched!"),
    Some(other) => println!("Other: {}", other),
    None => println!("None"),
}
// or
match user_email.as_deref().map(|ref_to_string| ref_to_string.as_str()) {
  // ...
}

(There is work happening towards having matching on smart pointers Just Work:tm: with the deref target as the type in the pattern, but it is not finished yet: #87121 )

(I wonder if there is T-libs-api appetite for something like Option::bikeshed_map_deref, since in my experience it is fairly common to have an Option<&T> and want an Option<&<T as Deref>::Target>, which Option::as_deref does not do. That alone wouldn't make the OP example a single call though; you'd still need to do like .as_deref().map_deref() to go from Option<Box<String>> to Option<&str>.)

Footnotes

  1. The reason you can call it on an Option<T> is due to auto-referencing when using the dot operator for method calls).

@VinEckSie
Copy link
Author

Thanks a lot for the clear explanation!

I’ll use that workaround and follow #87121 for future improvements.

@jieyouxu jieyouxu added C-discussion Category: Discussion or questions that doesn't represent real issues. and removed needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. C-bug Category: This is a bug. labels Apr 10, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-discussion Category: Discussion or questions that doesn't represent real issues.
Projects
None yet
Development

No branches or pull requests

4 participants