Read from an enum without pattern matching - enums

The Rust documentation gives this example where we have an instance of Result<T, E> named some_value:
match some_value {
Ok(value) => println!("got a value: {}", value),
Err(_) => println!("an error occurred"),
}
Is there any way to read from some_value without pattern matching? What about without even checking the type of the contents at runtime? Perhaps we somehow know with absolute certainty what type is contained or perhaps we're just being a bad programmer. In either case, I'm just curious to know if it's at all possible, not if it's a good idea.
It strikes me as a really interesting language feature that this branch is so difficult (or impossible?) to avoid.

At the lowest level, no, you can't read enum fields without a match1.
Methods on an enum can provide more convenient access to data within the enum (e.g. Result::unwrap), but under the hood, they're always implemented with a match.
If you know that a particular case in a match is unreachable, a common practice is to write unreachable!() on that branch (unreachable!() simply expands to a panic!() with a specific message).
1 If you have an enum with only one variant, you could also write a simple let statement to deconstruct the enum. Patterns in let and match statements must be exhaustive, and pattern matching the single variant from an enum is exhaustive. But enums with only one variant are pretty much never used; a struct would do the job just fine. And if you intend to add variants later, you're better off writing a match right away.
enum Single {
S(i32),
}
fn main() {
let a = Single::S(1);
let Single::S(b) = a;
println!("{}", b);
}
On the other hand, if you have an enum with more than one variant, you can also use if let and while let if you're interested in the data from a single variant only. While let and match require exhaustive patterns, if let and while let accept non-exhaustive patterns. You'll often see them used with Option:
fn main() {
if let Some(x) = std::env::args().len().checked_add(1) {
println!("{}", x);
} else {
println!("too many args :(");
}
}

Related

rust: If an enum has all variants derived from a generic struct, what's the best way to inspect the generic stored in the enum?

I've condensed things down to where I have a Generic Adapter, which is stored in an enum, that is non-generic. This looks like:
enum Collective {
StateTable(Adapter<StateTable>),
TestMessage(Adapter<TestMessage>)
}
I have getters and setters for Adapter, and I've stored Collective values in vector. While I can use match, to crack things out, via the following, I'm wondering if there isn't a better way.
impl Collective {
fn get_id(&self) -> u128 {
match self {
ReceiverAdapters::StateTable(a) => a.get_id(),
ReceiverAdapters::TestMessage(a) => a.get_id(),
}
}
I've considered From, but not sure if that will help. I'm also concerned with maintenance, as the number of variants grows. Can anyone provide an alternate to match? Or is match, under the covers compiling down to near-zero code in this case?

How to write fuctions that takes IntoIter more generally

I was reading an answer to stackoverflow question and tried to modify the function history to take IntoIter where item can be anything that can be transformed into reference and has some traits Debug in this case.
If I will remove V: ?Sized from the function definition rust compiler would complain that it doesn't know the size of str at compile time.
use std::fmt::Debug;
pub fn history<I: IntoIterator, V: ?Sized>(i: I) where I::Item: AsRef<V>, V: Debug {
for s in i {
println!("{:?}", s.as_ref());
}
}
fn main() {
history::<_, str>(&["st", "t", "u"]);
}
I don't understand why compiler shows error in the first place and not sure why the program is working properly if I kind of cheat with V: ?Sized.
I kind of cheat with V: ?Sized
It isn't cheating. All generic arguments are assumed to be Sized by default. This default is there because it's the most common case - without it, nearly every type parameter would have to be annotated with : Sized.
In your case, V is only ever accessed by reference, so it doesn't need to be Sized. Relaxing the Sized constraint makes your function as general as possible, allowing it to be used with the most possible types.
The type str is unsized, so this is not just about generalisation, you actually need to relax the default Sized constraint to be able to use your function with str.

Byte vs boolean for OrderBy

Is there any performance benefit to using a byte over a bool in ordering?
For example, given some code:
var foo = items.OrderByDescending(item => item.SomeProperty);
The existing code to get the value of SomeProperty is:
public byte SomeProperty
{
get
{
if (a == b)
return 1;
else
return 0;
}
}
I wanted to refactor this to:
public bool SomeProperty
{
get
{
a == b
}
}
I was told the first is more efficient. Is this true? Are there any downsides to using a bool over a byte?
The efficiency will hardly be in the processing efficiency. It will be more in efficiency of development code: is the code easy to understand? easy to reuse for similar items? easy to change if the internal structure changes without changing the interface? easy to test?
When designing a property your first question should be: what does my property stand for? What does it mean? Does it have an identifier and type that users will expect, or will they have to look it up in the documentation because they have no idea what it means?
For instance, if you have a class that represents something persistable, like a file, and you invent a property, which one will be easier to understand:
class Persistable
{
public int IsPersisted {get;}
public bool IsPersisted {get;}
...
Which one will readers immediately know what it means?
So for now your idea about persisted can have two values meaning "not persisted yet" and "persisted". A boolean will be enough. But if you foresee that in the near future the idea about persistence will change, for instance, the persistable can be "not persisted yet" "persisted" "changed after it has been persisted" "deleted". If you foresee that, you have to decide whether it is best to return a bool. Maybe your should return an enum:
public PersistencyState State {get;}
Conclusion Design the identifiers and types of your properties and methods such that the learning curve for your users is low, and that foreseeable changes don't have a great impact. Make sure that the properties are easy to test and maintain. In rare occasions portability is an issue.
Those items have bigger influence on your efficiency than the two code changes.
Back to your question
If you think about what SomeProperty represents, and you think: it represents the equality of a and b, then you should use:
public bool EqualAB => a == b
If your question is about whether you should use "get" or =>, the first one will call something sub-routine like, while the 2nd method will insert the code. If the part after the => is fairly big, and you use it on hundreds of locations, then your code will become bigger.
But then again: if your get is really big, should you make it a property?
public string ElderName
{
get
{
myDataBase.Open()
var allCustomers = myDataBase.FetchAllCustomers().ToList();
var eldestCustomer = this.FindEldestCustomer(allCustomers);
return eldestCustomer.Name;
}
}
Well this will have a fair impact on code size if you use the => notation on 1000 locations. But honestly, designers that put this in a property instead of a method don't deserve efficient code.
Finally, I asked here in stackoverflow whether there is a difference:
string Name {get => this.name;}
string Name => this.name;
The answer was that it translated into the same assembly code

Is it possible to use only one of the return values when initializing members in Go?

I understand how to use multiple return values in go. I further understand that in most cases one of the returns is an error, so ignoring returned values can be dangerous.
Is there a way to ignore a value in struct initializer like this? The below example does not work as Split returns two values, but I am interested only in the first one. I can of course create a variable but...
someFile := "test/filename.ext"
contrivedStruct := []struct{
parentDir string
}{
{ parentDir: filepath.Split(someFile) },
}
It's not possible to use only one of the return values when initializing members in Go.
Using variables clearly expresses your intent.
Go sometimes feels like it could be more succinct, but the Go authors favoured readability over brevity.
Alternatively, use a wrapper function. There are several 'Must' wrapper functions in the standard library, like: template.Must.
func first(args ...string) string {
return args[0]
}
For your particular example, splitting paths, see filepath.Base or filepath.Dir.
No, there is no way to skip one of the returned values in structure initializer.

Pattern matching over borrowed HashMap containing enums

I'm trying to learn Rust, so bear with me if I'm way off :-)
I have a program that inserts enums into a HashMap, and uses Strings as keys. I'm trying to match over the content of the HashMap. Problem is that I can't figure out how to get the correct borrowings, references and types in the eval_output function. How should the eval_output function look to properly handle a reference to a HashMap? Is there any good document that I can read to learn more about this particular subject?
use std::prelude::*;
use std::collections::HashMap;
enum Op {
Not(String),
Value(u16),
}
fn eval_output(output: &str, outputs: &HashMap<String, Op>) -> u16 {
match outputs.get(output) {
Some(&op) => {
match op {
Op::Not(input) => return eval_output(input.as_str(), outputs),
Op::Value(value) => return value,
}
}
None => panic!("Did not find input for wire {}", output),
}
}
fn main() {
let mut outputs = HashMap::new();
outputs.insert(String::from("x"), Op::Value(17));
outputs.insert(String::from("a"), Op::Not(String::from("x")));
println!("Calculated output is {}", eval_output("a", &outputs));
}
Review what the compiler error message is:
error: cannot move out of borrowed content [E0507]
Some(&op) => {
^~~
note: attempting to move value to here
Some(&op) => {
^~
help: to prevent the move, use `ref op` or `ref mut op` to capture value by reference
While technically correct, using Some(ref op) would be a bit silly, as the type of op would then be a double-reference (&&Op). Instead, we simply remove the & and have Some(op).
This is a common mistake that bites people, because to get it right you have to be familiar with both pattern matching and references, plus Rust's strict borrow checker. When you have Some(&op), that says
Match an Option that is the variant Some. The Some must contain a reference to a value. The referred-to thing should be moved out of where it is and placed into op.
When pattern matching, the two keywords ref and mut can come into play. These are not pattern-matched, but instead they control how the value is bound to the variable name. They are analogs of & and mut.
This leads us to the next error:
error: mismatched types:
expected `&Op`,
found `Op`
Op::Not(input) => return eval_output(input.as_str(), outputs),
^~~~~~~~~~~~~~
It's preferred to do match *some_reference, when possible, but in this case you cannot. So we need to update the pattern to match a reference to an Op — &Op. Look at what error comes next...
error: cannot move out of borrowed content [E0507]
&Op::Not(input) => return eval_output(input.as_str(), outputs),
^~~~~~~~~~~~~~~
It's our friend from earlier. This time, we will follow the compilers advice, and change it to ref input. A bit more changes and we have it:
use std::collections::HashMap;
enum Op {
Not(String),
Value(u16),
}
fn eval_output(output: &str, outputs: &HashMap<String, Op>) -> u16 {
match outputs.get(output) {
Some(op) => {
match op {
&Op::Not(ref input) => eval_output(input, outputs),
&Op::Value(value) => value,
}
}
None => panic!("Did not find input for wire {}", output),
}
}
fn main() {
let mut outputs = HashMap::new();
outputs.insert("x".into(), Op::Value(17));
outputs.insert("a".into(), Op::Not("x".into()));
println!("Calculated output is {}", eval_output("a", &outputs));
}
There's no need to use std::prelude::*; — the compiler inserts that automatically.
as_str doesn't exist in stable Rust. A reference to a String (&String) can use deref coercions to act like a string slice (&str).
I used into instead of String::from as it's a bit shorter. No real better reason.

Resources