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<_>`
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);
}
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
Related
I'm trying to match on the datatype of a generic field of a struct and react accordingly. My general idea was like this (code doesn't compile):
struct Foo<T> {
bar: T,
}
fn main() {
let x = Foo::<String> {
bar: "world".to_string(),
};
match x.bar {
String => println!("It's a string!"),
u32 => println!("It's a u32!"),
_ => println!("Something else"),
};
println!("end of program!");
}
The error message from the compiler:
warning: unreachable pattern
--> src/main.rs:12:9
|
11 | String => println!("It's a string!"),
| ------ matches any value
12 | u32 => println!("It's a u32!"),
| ^^^ unreachable pattern
|
= note: `#[warn(unreachable_patterns)]` on by default
warning: unreachable pattern
--> src/main.rs:13:9
|
11 | String => println!("It's a string!"),
| ------ matches any value
12 | u32 => println!("It's a u32!"),
13 | _ => println!("Something else"),
| ^ unreachable pattern
warning: unused variable: `String`
--> src/main.rs:11:9
|
11 | String => println!("It's a string!"),
| ^^^^^^ help: consider prefixing with an underscore: `_String`
|
= note: `#[warn(unused_variables)]` on by default
warning: unused variable: `u32`
--> src/main.rs:12:9
|
12 | u32 => println!("It's a u32!"),
| ^^^ help: consider prefixing with an underscore: `_u32`
warning: variable `String` should have a snake case name
--> src/main.rs:11:9
|
11 | String => println!("It's a string!"),
| ^^^^^^ help: convert the identifier to snake case: `string`
|
= note: `#[warn(non_snake_case)]` on by default
What I wanted was for x to match the first one. I'm actually not sure what I want to do can be done, but what would achieve the desired effect?
Idiomatic Solution
Create a trait which constrains the parameter T in Foo, implement any specific behavior as an associated function of this trait.
Example:
trait PrintMe {
fn print_me(&self);
}
impl PrintMe for String {
fn print_me(&self) { println!("I am a string"); }
}
struct Foo<T: PrintMe> {
bar: T
}
fn main() {
// ...
x.bar.print_me();
}
This is principled generic programming, where you declare exactly the difference of behavior of the possible generic parameters, so that there is no surprise.
See also:
Why is this match pattern unreachable when using non-literal patterns?
Exact Solution
Rust can indeed query types: each type has a unique TypeId associated, and you can match on TypeId with a series of if checks. It's clunky.
fn print_me<T>(x: &Foo<T>) {
if TypeId::of::<T>() == TypeId::of::<String>() {
println!("I am a string");
} else // ...
}
But please... don't do that :)
A more general look at matching over types
Basically, you hardly can match on the type, instead you have to use rust's traits.
Here is a helpful introduction for Rust's traits:
https://doc.rust-lang.org/rust-by-example/trait.html
I will explain simply how to match over type so it's okay if you don't know how to use traits.
Here is how to more generally change behavior depending on the type:
Define a trait that is going to do something depending on the type.
Add a function that takes in what you need to do something with it.
It will take the variable with a type that varies as self. Don't add a body to that function, just follow the definition with a semicolon ;.
trait DoSomething {
fn someFunction(&self);
}
Implement the trait for all the types you wanted to match for: String and u32.
This is the part where you can code different behavior for each type.
impl DoSomething for String {
fn someFunction(&self) {
println!("It is a string!")
}
}
impl DoSomething for u32 {
fn someFunction(&self) {
println!("It is a u32!")
}
}
Instead of matching on an element, call the method in the trait on it.
struct Foo<T> {
bar: T,
}
fn main() {
let x = Foo::<String> {
bar: "world".to_string(),
};
// the call of the method someFunction will differentiate the type of x.bar
x.bar.someFunction();
}
TLDR: in Rust, to match over type, we create a trait, implement a function for each type and call it on the element to match.
I wonder if there is a way to simplify the following pattern match arms when two or more different enum's types have the same data member or same function.
(if not it will be nice to explain why)
UPDATE:
as requested a more accurate example of what i want (forgive me for confusing data member access with function) (try it online):
struct Point<T> {
x: i32,
y: T,
}
enum Record {
V4(Point<i64>),
V6(Point<i32>),
}
fn get_record() -> Record {
Record::V4(Point{ x: 1, y: 1})
}
fn main() {
let x = match get_record() {
Record::V4(r) => r.x,
Record::V6(r) => r.x,
};
println!("{}", &x);
// this will not compile
// let rec = get_record();
// println!("{}", rec.x);
// this will not compile either
// note: if V4 Point was i32 it will compile & run
// let rec = get_record();
// let x = match get_record() {
// Record::V4(r) | Record::V6(r) => r.x,
// };
}
Original Post:
use std::net::IpAddr;
use std::str::FromStr;
fn main() {
let v4_or_v6 = IpAddr::from_str("1.2.3.4").unwrap();
// match expression, both arms only differ by 1 char
let s = match v4_or_v6 {
IpAddr::V4(ip) => ip.to_string(),
IpAddr::V6(ip) => ip.to_string(),
};
println!("{}", &s);
// not working:
// let s2 = match v4_or_v6 {
// IpAddr::V4(ip) | IpAddr::V6(ip) => ip.to_string(),
// };
// println!("{}", &s2);
}
I understand that the underlying call to to_string() has different implementation for Ipv4 than Ipv6 but i think the compiler can be smart enough to handle this (am i wrong?)
trying to compile with the commented out code results in compilation error (try it online):
Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
--> src/main.rs:16:37
|
16 | IpAddr::V4(ip) | IpAddr::V6(ip) => ip.to_string(),
| ^^ expected struct `std::net::Ipv4Addr`, found struct `std::net::Ipv6Addr`
|
= note: expected type `std::net::Ipv4Addr`
found type `std::net::Ipv6Addr`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.
error: Could not compile `playground`.
The working code de-sugars to:
let s = match v4_or_v6 {
IpAddr::V4(ip) => <Ipv4Addr as ToString>::to_string(&ip),
IpAddr::V6(ip) => <Ipv6Addr as ToString>::to_string(&ip),
};
Even though the statements look the same, they are different functions and in each branch it is known statically which to_string is going to be used. To get this to work in a single match arm, you would have to somehow produce a trait object from the pattern match, so that each ip has the same type (i.e. &dyn ToString). Currently there isn't a way to do that and I haven't seen any proposal like it.
It's pretty common to see identical-looking match arms, where the same trait method is called on each, even in the rustc project. This is just how it is, for now.
If you have an enum where each variant holds types that implement the same traits, it might be convenient to implement the traits on the enum and delegate to the inner types. If you don't have a trait but your types have common structure (as in the x, y fields in the struct of your updated post), then you can provide an accessor on the enum:
impl Record {
fn x(&self) -> i32 {
match self {
Record::V4(Point { x, .. }) => *x,
Record::V6(Point { x, .. }) => *x,
}
}
}
While this is basically the same thing, it means you can write it once instead of everywhere that you need to access x:
let rec = get_record();
let x = get_record().x();
Note that IpAddr already does this so, in your original code, you could have avoided the match altogether with:
let s = v4_or_v6.to_string();
I have the following enum defined:
#[derive(Debug, Copy, Clone)]
struct Core;
#[derive(Debug, Copy, Clone)]
struct Mem;
#[derive(Debug, Copy, Clone)]
pub enum Atag {
Core(Core),
Mem(Mem),
Cmd(&'static str),
Unknown(u32),
None,
}
I would like to implement a function on this enum which "filters out" certain enum values. I have the following:
impl Atag {
/// Returns `Some` if this is a `Core` ATAG. Otherwise returns `None`.
pub fn core(self) -> Option<Core> {
match self {
Atag::Core => Some(self),
_ => None
}
}
}
I'm not sure why, but the compiler complains:
error[E0532]: expected unit struct/variant or constant, found tuple variant `Atag::Core`
--> src/main.rs:17:13
|
17 | Atag::Core => Some(self),
| ^^^^^^^^^^ not a unit struct/variant or constant
help: possible better candidate is found in another module, you can import it into scope
|
1 | use Core;
|
I also tried a comparison approach:
pub fn core(self) -> Option<Core> {
if self == Atag::Core {
Some(self)
} else {
None
}
}
But the compiler complains:
error[E0369]: binary operation `==` cannot be applied to type `Atag`
--> src/main.rs:20:12
|
20 | if self == Atag::Core {
| ^^^^^^^^^^^^^^^^^^
|
= note: an implementation of `std::cmp::PartialEq` might be missing for `Atag`
I think this is just a limitation of the pattern matching and is designed to prevent unexpected behavior.
The full "definition" of an Atag with type Core is Atag::Core(raw::Core). Obviously, the contents of the Core are irrelevant to you, but the compiler needs to know that everything is "accounted for" because the compiler is a stickler for the rules. The easiest way to get around this is to use the "anything pattern", _, much like you did to match non-Core variants.
impl Atag {
/// Returns `Some` if this is a `Core` ATAG. Otherwise returns `None`.
pub fn core(self) -> Option<Core> {
match self {
// The compiler now knows that a value is expected,
// but isn't necessary for the purposes of our program.
Atag::Core(_) => Some(self),
_ => None
}
}
}
To ignore multiple values, you'd use Something::Foo(_, _) - one underscore for each value in the variant, or Something::Foo(..) to ignore everything.
Remember that, unlike in some other languages, a Rust enum is not "just" a collection of different types. Data associated with an enum value is a part of it, just like the fields of a structure. So self == Atag::Core isn't a meaningful statement because it ignores the data associated with a Core. A Foo(0) is different than a Foo(12), even if they're both of the Foo variant.
I'd also like to point out if let, which is - as far as I can tell - the closest option to a standard if statement without defining a custom is_core function on Atag (which, given the existence of match and if let, is basically unnecessary).
impl Atag {
/// Returns `Some` if this is a `Core` ATAG. Otherwise returns `None`.
pub fn core(self) -> Option<Core> {
if let Atag::Core(_) = self {
Some(self)
} else {
None
}
}
}
I needed something like this to chain functions together nicely. In that case, you want to return the unwrapped core type, rather than just the enum.
I also found it easier to not consume the input, and so accepted a &self argument an returned an Option<&Core>. But you can have both.
The Rust convention has as_X as the reference-based conversion and into_X as the conversion that consumes the value. For example:
impl Atag {
fn as_core(&self) -> Option<&Core> {
if let Atag::Core(ref v) = self {
Some(v)
} else {
None
}
}
fn into_core(self) -> Option<Core> {
if let Atag::Core(v) = self {
Some(v)
} else {
None
}
}
}
fn main() {
let c = Atag::Core(Core {});
let m = Atag::Mem(Mem {});
assert_eq!(c.as_core().map(|cc| "CORE_REF"), Some("CORE_REF"));
assert_eq!(m.as_core().map(|cc| "CORE_REF"), None);
// Consume c - we cant use it after here...
assert_eq!(c.into_core().map(|cc| "NOM NOM CORE"), Some("NOM NOM CORE"));
// Consume m - we cant use it after here...
assert_eq!(m.into_core().map(|cc| "NOM NOM CORE"), None);
}
I'm trying to match on the datatype of a generic field of a struct and react accordingly. My general idea was like this (code doesn't compile):
struct Foo<T> {
bar: T,
}
fn main() {
let x = Foo::<String> {
bar: "world".to_string(),
};
match x.bar {
String => println!("It's a string!"),
u32 => println!("It's a u32!"),
_ => println!("Something else"),
};
println!("end of program!");
}
The error message from the compiler:
warning: unreachable pattern
--> src/main.rs:12:9
|
11 | String => println!("It's a string!"),
| ------ matches any value
12 | u32 => println!("It's a u32!"),
| ^^^ unreachable pattern
|
= note: `#[warn(unreachable_patterns)]` on by default
warning: unreachable pattern
--> src/main.rs:13:9
|
11 | String => println!("It's a string!"),
| ------ matches any value
12 | u32 => println!("It's a u32!"),
13 | _ => println!("Something else"),
| ^ unreachable pattern
warning: unused variable: `String`
--> src/main.rs:11:9
|
11 | String => println!("It's a string!"),
| ^^^^^^ help: consider prefixing with an underscore: `_String`
|
= note: `#[warn(unused_variables)]` on by default
warning: unused variable: `u32`
--> src/main.rs:12:9
|
12 | u32 => println!("It's a u32!"),
| ^^^ help: consider prefixing with an underscore: `_u32`
warning: variable `String` should have a snake case name
--> src/main.rs:11:9
|
11 | String => println!("It's a string!"),
| ^^^^^^ help: convert the identifier to snake case: `string`
|
= note: `#[warn(non_snake_case)]` on by default
What I wanted was for x to match the first one. I'm actually not sure what I want to do can be done, but what would achieve the desired effect?
Idiomatic Solution
Create a trait which constrains the parameter T in Foo, implement any specific behavior as an associated function of this trait.
Example:
trait PrintMe {
fn print_me(&self);
}
impl PrintMe for String {
fn print_me(&self) { println!("I am a string"); }
}
struct Foo<T: PrintMe> {
bar: T
}
fn main() {
// ...
x.bar.print_me();
}
This is principled generic programming, where you declare exactly the difference of behavior of the possible generic parameters, so that there is no surprise.
See also:
Why is this match pattern unreachable when using non-literal patterns?
Exact Solution
Rust can indeed query types: each type has a unique TypeId associated, and you can match on TypeId with a series of if checks. It's clunky.
fn print_me<T>(x: &Foo<T>) {
if TypeId::of::<T>() == TypeId::of::<String>() {
println!("I am a string");
} else // ...
}
But please... don't do that :)
A more general look at matching over types
Basically, you hardly can match on the type, instead you have to use rust's traits.
Here is a helpful introduction for Rust's traits:
https://doc.rust-lang.org/rust-by-example/trait.html
I will explain simply how to match over type so it's okay if you don't know how to use traits.
Here is how to more generally change behavior depending on the type:
Define a trait that is going to do something depending on the type.
Add a function that takes in what you need to do something with it.
It will take the variable with a type that varies as self. Don't add a body to that function, just follow the definition with a semicolon ;.
trait DoSomething {
fn someFunction(&self);
}
Implement the trait for all the types you wanted to match for: String and u32.
This is the part where you can code different behavior for each type.
impl DoSomething for String {
fn someFunction(&self) {
println!("It is a string!")
}
}
impl DoSomething for u32 {
fn someFunction(&self) {
println!("It is a u32!")
}
}
Instead of matching on an element, call the method in the trait on it.
struct Foo<T> {
bar: T,
}
fn main() {
let x = Foo::<String> {
bar: "world".to_string(),
};
// the call of the method someFunction will differentiate the type of x.bar
x.bar.someFunction();
}
TLDR: in Rust, to match over type, we create a trait, implement a function for each type and call it on the element to match.
Faced a weird case of repeated argument mismatch while creating a macro in Rust:
use std::mem;
trait Object {}
#[derive(Debug)]
struct This {}
impl Object for This {}
#[derive(Debug)]
struct That {}
impl Object for That {}
macro_rules! types {
($($fname:ident),*) => {
enum Type {
$($fname),*
}
fn match_it(t: Type, b: Box<Object>) {
let p = match t {
$(
Type::$fname => {
mem::transmute::<Box<Object>, Box<$fname>>(b)
}
),*
};
}
}
}
types!(This, That);
fn main() {}
It results in:
error: match arms have incompatible types [--explain E0308]
--> <anon>:20:21
20 |> let p = match t {
|> ^ expected struct `This`, found struct `That`
<anon>:31:1: 31:20: note: in this expansion of types! (defined in <anon>)
note: expected type `Box<This>`
note: found type `Box<That>`
note: match arm with an incompatible type
--> <anon>:22:33
22 |> Type::$fname => {
|> ^
<anon>:31:1: 31:20: note: in this expansion of types! (defined in <anon>)
Shouldn't the $fname of enum be the same as $fname of Box if they share the same loop?
Play it.
The macro expands to something like:
enum Type {This, That }
fn match_it(t: Type, b: Box<Object>) {
let p = match t {
Type::This => mem::transmute::<Box<Object>, Box<This>>(b),
Type::That => mem::transmute::<Box<Object>, Box<That>>(b),
}
}
What's the type of p? Depending on something at runtime, the compile-time type must be different; this doesn't make sense in a statically typed language like Rust.
I suggest looking into std::any, which seems similar to what you might be trying to do.
As an alternative, maybe you want runtime casting using Box<Any>:
use std::any::Any;
struct N(isize);
struct S(String);
fn main() {
let mut v: Vec<Box<Any>> = Vec::new();
v.push(Box::new(N(17)));
v.push(Box::new(S("foo".to_string())));
let s = v.pop().unwrap().downcast::<S>().unwrap();
let n = v.pop().unwrap().downcast::<N>().unwrap();
println!("Extracted {} and {}", s.0, n.0);
}
Play link