I am learning Rust and I am trying to write a simple IP handling function
enum IpAddr{
v4(u8,u8,u8,u8),
v6(String),
}
impl IpAddr{
fn write(&self){
match *self {
IpAddr::v4(A,B,C,D) => println!("{}.{}.{}.{}",A,B,C,D),
IpAddr::v6(S) => println!("{}",S)
}
}
}
The v4 matches fine, but I get the following build error on the 2nd one
error[E0507]: cannot move out of self.0 which is behind a shared reference
move occurs because _S has type String, which does not implement the Copy trait
How can I match on a enum with a String attached?
Its complaining because you are trying to copy self by dereferencing, but IpAddr contains a String which is not copy-able.
Remove the dereference and it should work as expected
match self {
IpAddr::v4(A,B,C,D) => println!("{}.{}.{}.{}",A,B,C,D),
IpAddr::v6(S) => println!("{}",S)
}
I'm not sure why you're matching on *self, as it does not appear to be necessary, but if you really need to do that for some reason, you can also solve the problem by using the ref keyword with your variable (ref s), which will cause the value to be borrowed instead of moving it.
enum IpAddr{
V4(u8,u8,u8,u8),
V6(String),
}
impl IpAddr{
fn write(&self){
match *self {
IpAddr::V4(a,b,c,d) => println!("{}.{}.{}.{}",a,b,c,d),
IpAddr::V6(ref s) => println!("{}", s)
}
}
}
Playground
Related
Context
Link: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=9a9ffa99023735f4fbedec09e1c7ac55
Here's a contrived repro of what I'm running into
fn main() {
let mut s = String::from("Hello World");
example(&mut s);
}
fn example(s: &mut str) -> Option<String> {
other_func(Some(s.to_owned()))
// other random mutable stuff happens
}
fn other_func(s: Option<String>) {
match s {
Some(ref s) => other_func2(*s),
None => panic!()
}
}
fn other_func2(s: String) {
println!("{}", &s)
}
and the error
Compiling playground v0.0.1 (/playground)
error[E0507]: cannot move out of `*s` which is behind a shared reference
--> src/main.rs:12:36
|
12 | Some(ref s) => other_func2(*s),
| ^^ move occurs because `*s` has type `String`, which does not implement the `Copy` trait
Question
In the following code, why can't I deference the &String without having to do some sort of clone/copy? i.e. this doesn't work
fn other_func(s: Option<String>) {
match s {
Some(ref s) => other_func2(*s),
None => panic!()
}
}
but it works if I replace *s with s.to_owned()/s.to_string()/s.clone()
As an aside, I understand this can probably be solved by refactoring to use &str, but I'm specifically interested in turning &String -> String
Why would the compiler allow you to?
s is &String. And you cannot get a String from a &String without cloning. That's obvious.
And the fact that it was created from an owned String? The compiler doesn't care, and it is right. This is not different from the following code:
let s: String = ...;
let r: &String = ...;
let s2: String = *r; // Error
Which is in turn not different from the following code, for instance, as far as the compiler is concerned:
let r: &String = ...;
let s: String = *s;
And we no longer have an owned string at the beginning. In general, the compiler doesn't track data flow. And rightfully so - when it type-checks the move it doesn't even can confirm that this reference isn't aliased. Or that the owned value is not used anymore. References are just references, they give you no right to drop the value.
Changing that will not be feasible in the general case (for example, the compiler will have to track data flow across function calls), and will require some form of manual annotation to say "this value is mine". And you already have such annotation - use an owned value, String, instead of &String: this is exactly what it's about.
use crate::List::{Cons, Nil};
#[derive(Debug)]
struct Foo {}
#[derive(Debug)]
enum List {
Cons(i32, Foo),
Nil,
}
impl List {
fn tail(&self) -> Option<&Foo> {
match self {
Cons(_, item) => Some(item), // why `item` is of type `&Foo`?
Nil => None,
}
}
}
As stated in the comment, why is item of type &Foo? What is the rule that says item will be type &Foo rather than Foo?
I understand that it does not make sense for item to be Foo; &self says self is a reference, so it does not make sense to move a value out of a reference, but are there any specifications that define the rules clearly?
RFC 2005 (a.k.a. match ergonomics) introduced the rule.
Before the change was implemented, there were 2 ways to write this match.
Match on self and prefix each pattern with & to "destructure" the reference.
fn tail(&self) -> Option<&Foo> {
match self {
&Cons(_, ref item) => Some(item),
&Nil => None,
}
}
Match on *self and don't prefix each pattern with & (because *self is not a reference).
fn tail(&self) -> Option<&Foo> {
match *self {
Cons(_, ref item) => Some(item),
Nil => None,
}
}
Yet, in both cases, we need to write ref item, otherwise we'll get error[E0507]: cannot move out of borrowed content.
However, in the match you've written, the expression being matched is a reference (type &List) but the patterns are not reference patterns (as in 1. above). This is where match ergonomics kick in: the rule says that when a reference is matched with a non-reference pattern, the bindings within that pattern bind by reference rather than by value (i.e. as if they were prefixed with ref).
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);
}
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