5

The docs for Arc<T> say:

Arc<T> automatically dereferences to T (via the Deref trait), so you can call T's methods on a value of type Arc<T>.

But is there any way to allow for matching on Option-al types?

Here is a simple example:

use std::sync::Arc;

fn main() {
    let foo: Arc<Option<String>> = Arc::new(Some("hello".to_string()));

    if foo.is_some() {
        println!("{}", foo.unwrap());
    }

    match foo {
        Some(hello) => {
            println!("{}", hello);
        }
        None => {}
    }
}

The compiler error is:

error[E0308]: mismatched types
  --> src/main.rs:11:9
   |
11 |         Some(hello) => {
   |         ^^^^^^^^^^^ expected struct `std::sync::Arc`, found enum `std::option::Option`
   |
   = note: expected type `std::sync::Arc<std::option::Option<std::string::String>>`
              found type `std::option::Option<_>`

error[E0308]: mismatched types
  --> src/main.rs:14:9
   |
14 |         None => {}
   |         ^^^^ expected struct `std::sync::Arc`, found enum `std::option::Option`
   |
   = note: expected type `std::sync::Arc<std::option::Option<std::string::String>>`
              found type `std::option::Option<_>`
Shepmaster
  • 274,917
  • 47
  • 731
  • 969
marathon
  • 6,497
  • 10
  • 56
  • 120

2 Answers2

8

No, you cannot match on an Option inside of an Arc. To use a type in pattern matching, the implementation of the type must be available to you, but the implementation of Arc is not public.


In certain cases, you can perform some kind of conversion to be able to match on a reference.

For example, since Arc<T> implements Deref, you can use the * operator to dereference through the Arc<T> to the underlying T. Since there's some ergonomic syntax for this kind of matching, you can then take a reference to the value inside the Option without taking ownership of it:

match *foo {
    Some(ref hello) => {
        println!("{}", hello);
    }
    None => {}
}

You can also use Option::as_ref to convert the &Option<T> (automatically dereferenced from the Arc<T> via Deref) to an Option<&T>:

match Option::as_ref(&foo) {
    Some(hello) => {
        println!("{}", hello);
    }
    None => {}
}

Unfortunately, you can't just call .as_ref() because the trait method AsRef::as_ref takes precedence.

In both cases, it's more idiomatic to use if let if you only care about one of the match arms:

if let Some(ref hello) = *foo {
    println!("{}", hello);
}
Shepmaster
  • 274,917
  • 47
  • 731
  • 969
3

The first println passed the early stages of the compiler, but it got flagged by the borrow checker in a later stage. The second println was an easier fix.

use std::sync::Arc;

fn main() {
    let foo: Arc<Option<String>> = Arc::new(Some("hello".to_string()));

    if foo.is_some() {
        let f1: &Option<String> = foo.as_ref();
        let f2: Option<&String> = f1.as_ref();
        let f3: &String = f2.unwrap();
        println!("{}", f3);

        println!("{}", foo.as_ref().as_ref().unwrap())
    }

    match *foo {
        Some(ref hello) => {
            println!("{}", hello);
        }
        None => {}
    }
}

The first println confusingly uses two as_ref() method calls. The first as_ref acts on the Arc, with type signature Fn(&Arc<Option<String>>) -> &Option<String>. The second one acts on the Option, with type signature Fn(&Option<String>) -> Option<&String>

playground

Shepmaster
  • 274,917
  • 47
  • 731
  • 969
NovaDenizen
  • 4,555
  • 12
  • 24
  • 2
    Dereferencing `foo` (as in `let f2 = (*foo).as_ref()`)is a little more idiomatic than using `.as_ref` on an `Arc`. – trentcl Jan 27 '18 at 02:04