Sharing a common value in all enum values - enums

I have the following code where every variant of the enum Message has a Term value associated with it:
type Term = usize;
pub enum Message {
AppendRequest(Term),
AppendResponse(Term),
VoteRequest(Term),
VoteResponse(Term),
}
impl Message {
pub fn term(&self) -> Term {
match *self {
Message::AppendRequest(term) => term,
Message::AppendResponse(term) => term,
Message::VoteRequest(term) => term,
Message::VoteResponse(term) =>term,
}
}
}
I want to, given a Message be able to get its term without having to deconstruct the actual Message value I have. The best I could come up with was creating a public function that unpacked the value for me, but this feels clunky. If I ever add a new enum value, I'm going to have to remember to update match statement in the term function.
Is there a more succinct/ergonomic way to express the code above? Is there some way to say "hey, every value for this enum will have also have a Term value associated with it.

Is there some way to say "hey, every value for this enum will have also have a Term value associated with it.
No. This is usually handled by splitting the enum into two parts, with a struct containing all the common parts:
pub struct Message {
term: Term,
kind: MessageKind,
}
pub enum MessageKind {
AppendRequest,
AppendResponse,
VoteRequest,
VoteResponse,
}

One option is to implement the Deref (and/or DerefMut) trait to convert to the common part.
You still have to update that implementation each time you add to the Enum, but there is less boilerplate at the point of use.
E.g., an example below, note that main accesses the field number on the Enum.
use std::ops::Deref;
use std::string::String;
enum JudgedNumber {
GoodNumber(Number),
BadNumber(Number, String),
}
struct Number { number: i32 }
fn main() {
let nice = JudgedNumber::GoodNumber(Number{number: 42});
let naughty = JudgedNumber::BadNumber(
Number{number: 666}, "Damn you to hell".to_string());
println!("j1 = {}", j1.number);
println!("j2 = {}", j2.number);
}
impl Deref for JudgedNumber {
type Target = Number;
fn deref(&self) -> &Number {
match self {
JudgedNumber::GoodNumber(n) => n,
JudgedNumber::BadNumber(n, _) => n,
}
}
}
I learnt this from https://github.com/rust-embedded/svd/blob/master/src/svd/cluster.rs

Related

How do I filter a vector of an enum of different structs using a common field?

I found you can create a vector of different types of structs using an enum. When filtering the vector on a common field, such as id, the compiler doesn't know the type while iterating:
use chrono::{DateTime, Utc}; // 0.4.19
use serde::{Serialize, Deserialize}; // 1.0.126
#[derive(Deserialize, Debug, Serialize, Clone)]
pub enum TransactionsEnum {
TransactionOrderA(TransactionOrderA),
TransactionOrderB(TransactionOrderB),
}
#[derive(Deserialize, Debug, Serialize, Clone)]
pub struct TransactionOrderA {
pub id: i64,
pub accountID: String,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TransactionOrderB {
pub id: i64,
pub time: DateTime<Utc>,
}
fn transactions_filter(
transactions_vector: Vec<TransactionsEnum>,
x: i64,
) -> Vec<TransactionsEnum> {
transactions_vector
.into_iter()
.filter(|e| e.id >= x)
.collect()
}
error[E0609]: no field `id` on type `&TransactionsEnum`
--> src/lib.rs:27:23
|
27 | .filter(|e| e.id >= x)
| ^^
Sharing a common value in all enum values and Is there a way to directly access a field value in an enum struct without pattern matching? indirectly answer my question, but the answer provided here helped me understand why a match statement is necessary.
Those are not a common field, they are completely unrelated fields. The fact that they share a name is, as far as the Rust compiler is concerned, an insignificant coincidence. You need to use pattern matching to get the field from either case
impl TransactionsEnum {
pub fn id(&self) -> i64 {
match self {
TransactionsEnum::TransactionOrderA(value) => value.id,
TransactionsEnum::TransactionOrderB(value) => value.id,
}
}
}
transactions_vector
.into_iter()
.filter(|e| e.id() >= x) // Note the parentheses since we're calling a function now
.collect()

Want to know about automatic drop of data that no one references

I don't think the garbace collection of rust is fully explained with only the scope of the ownership.
I have googled it, and this is what I've got.
[ temporary data ]
If you reference temporary data, lifetime of the data differs by the expression where it comes : at the end of the scope, or at the end of the expression.
Look for the further explanation in here :
https://doc.bccnsoft.com/docs/rust-1.36.0-docs-html/reference/expressions.html#temporary-lifetimes
[ reassignment of variables ]
let a = String::from("first");
a = String::from("second");
In above case, first string data automatically drops while second assignment.
However, I couldn't search for more information since then.
In my prediction,
Fields of structures and indexes of arrays may be considered as "an independent variable" so that changing them to be considered as an reassignment of variable.
struct A {
a: String,
b: String
}
let mut x = A {
a: String::from("first"),
b: String::from("second")
}
x.a = String::from("reassignment"); // first string drops here
Also, we all know that if a variable drops, it drops all of its contents. (as below)
{
let a = vec!(String::from("first"), String::from("second"));
} // all the strings are dropped here.
OK. Then what about more complicated stuffs??
Box<T> or HashMap<String, i32> ... etc... which is data that consumes ownership of anyother data.
What if we change its inner data??
Is it the same act as reassigning a field of structure??
I wonder if they're just complex structures or totally different objects.
Is there any other rules of "auto drop of data" I should know?
If you want to known more and observe on automatic dropping then I recommend implementing Drop trait for a type. Rust doesn't have any garbage collector but the compiler does that job of adding instructions to cleanup the objects when they go out of scope.
Check this Rust RAII
If you implement Drop trait and use logging or printing in drop fn then you known when it goes out of scope and reclaimed. In below example observe when the drop method of Data struct gets called when the object goes out of scope.
use std::collections::HashMap;
#[derive(Debug)]
struct Data{
val: String
}
impl Drop for Data {
fn drop(&mut self) {
println!("Dropping for {:?}", self.val);
}
}
#[async_std::main]
async fn main() -> std::io::Result<()> {
{
let mut dataref = Data{val: "d0".to_string()};
{
let data = Data{val: "d1".to_string()};
let data2 = Data{val: "d2".to_string()};
dataref = data2;
println!("scope marker 1");
}
println!("scope marker 2");
let mut map = HashMap::new();
map.insert("k1".to_string(), Data{val:"v1".to_string()});
println!("Before chaning for k1");
map.insert("k1".to_string(), Data{val:"v1".to_string()});
println!("Changed k1");
}
Ok(())
}

Why do we use the Option enum?

I don't get what the Option enum is for. I read that Rust doesn't have null values. The Option enum is defined like this:
enum Option<T> {
Some(T),
None,
}
I read its implementation and I came across this example:
fn main() {
fn divide(numerator: f64, denominator: f64) -> Option<f64> {
if denominator == 0.0 {
None
} else {
Some(numerator / denominator)
}
}
// The return value of the function is an option
let result = divide(2.0, 3.0);
// Pattern match to retrieve the value
match result {
// The division was valid
Some(x) => println!("Result: {}", x),
// The division was invalid
None => println!("Cannot divide by 0"),
}
}
When they could also do it like this:
fn main() {
fn divide(numerator: f64, denominator: f64) -> String {
if denominator == 0.0 {
format!("Can't divide")
} else {
let x = numerator / denominator;
format!("{}", x)
}
}
let result = divide(2.0, 3.0);
println!("{}", result);
}
Both programs output:
0.6666666666666666
Maybe the above example is not a very good example of Option, but the following example shows Option at its very best:
fn main() {
let name = String::from("naufil");
println!(
"Character at index 6: {}",
match name.chars().nth(6) {
Some(c) => c.to_string(),
None => "No character at index 6!".to_string(),
}
)
}
When we are not sure whether there is a character at 6th element and you don't want your program to crash, Option comes to the rescue. Here is another example from The Rust Programming Language:
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
None => None,
Some(i) => Some(i + 1),
}
}
let five = Some(5);
let six = plus_one(five);
let none = plus_one(None);
Listing 6-5: A function that uses a match expression on
an Option<i32>
Let’s examine the first execution of plus_one in more detail. When we call
plus_one(five), the variable x in the body of plus_one will have the
value Some(5). We then compare that against each match arm.
None => None,
The Some(5) value doesn’t match the pattern None, so we continue to the
next arm.
Some(i) => Some(i + 1),
Does Some(5) match Some(i)? Why yes it does! We have the same variant. The
i binds to the value contained in Some, so i takes the value 5. The
code in the match arm is then executed, so we add 1 to the value of i and
create a new Some value with our total 6 inside.
Now let’s consider the second call of plus_one in Listing 6-5, where x is
None. We enter the match and compare to the first arm.
None => None,
It matches! There’s no value to add to, so the program stops and returns the
None value on the right side of =>. Because the first arm matched, no other
arms are compared.
Combining match and enums is useful in many situations. You’ll see this
pattern a lot in Rust code: match against an enum, bind a variable to the
data inside, and then execute code based on it. It’s a bit tricky at first, but
once you get used to it, you’ll wish you had it in all languages. It’s
consistently a user favorite.
The reason the Option enum is used for the same reason the Result enum is used. It allows the programmer to see the breadth of returning values they might receive, but without having to dig through code you don't remember all the details about, or have never seen.
Option isn't a special value, it's just an enum, like Result. You could also use something like:
enum Division_Result {
Successful(f64),
DividedByZero,
}
fn divide(numerator: f64, denominator: f64) -> Division_Result {
if denominator == 0.0 {
Division_Result::DividedByZero
} else {
Division_Result::Successful(numerator / denominator)
}
}
It just so happens that Optional values are some of the most common types of values that you have to deal with in a program. They're baked the Optional enum into standard because otherwise you would have to deal with everyone coming up with their own enum for the simple concept of an Optional value.
Returning an enum is an improvement over returning unwrapped magic values because it is more explicit to the programmer that the return value might diverge from what they wanted from the function.

Conditional compilation with a if let enum matching which consists of one item

I have the following enum:
pub enum Game {
Match(GameWorker),
#[cfg(feature = "cups")]
Cup(CupWorker),
}
So, this enum consists of one item if cups feature is disabled. The code below with match compiles okay but in place where I use if lets on matching this enum there is a error:
Working match:
fn clear(&mut self, silent: bool) {
match *self {
Game::Match(ref mut gm) => gm.clear(silent),
#[cfg(feature = "cups")]
Game::Cup(ref mut c) => c.clear(silent),
}
}
if let which leads to a compile error:
let m: &mut Game = Game::Match(...);
if let Game::Match(ref mut gamematch) = *m {
// ...
}
Error:
error[E0162]: irrefutable if-let pattern
--> src/game.rs:436:32
|
436 | if let Game::Match(ref mut gamematch) = *m {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ irrefutable pattern
Minimal example
Is there a way to allow such if lets ? I like this construction but somewhy it is not allowed to use it, I don't understand why. As shown above, match construction works okay in the same case. In my personal opinion here should be a silenceable warning instead of error.
if let expects a refutable pattern, similar to how if expects a bool. You can't write if () { something }, even though () is "valid" in some sense. If you had if () {} else { something_else } it would be statically known that the else cannot occur.
Arguably if true { something } is also statically known, but there's a difference: The condition is a bool, which has two values, so even if you statically know the value, the type still offers multiple variants.
With if let it's the same, but you can use user defined types instead of just bool. If your enum has multiple variants, you can't statically decide that the if let is always taken. If the enum has a single variant, you know for a fact that the if condition is always true, so even if you had an else branch, it would not make any sense at all to exist.

How to extend the lifetimes of Strings within functions of traits that require returning &str?

Problem
I am trying to implement the std::error::Error trait on a enum. Some elements of the enum are Enum Variants, and I would like to generate a different error message containing the data from the variant. However with the implementation below the formatted String that Deref to &str don't live long enough.
The general solution is to return a String. However, this is not an option here as the returned type must be &str as specified by the Error trait.
Example: Playground link
It is important to note that the variants may not contain usize, and might instead be another enum, or struct etc.
use std::fmt;
use std::fmt::{Display, Formatter};
use std::error;
#[derive(Debug)]
enum EnumError {
A,
B(usize),
C(usize),
D,
}
impl error::Error for EnumError {
fn description(&self) -> &str {
use EnumError::*;
match *self {
A => "A happened",
B(value) => &*format!("B happened info: {:?}", value),
C(value) => &*format!("B happened info: {:?}", value),
D => "D happened",
}
}
}
impl Display for EnumError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
use std::error::Error;
write!(f, "{}", self.description())
}
}
fn main() {}
The string you create needs to be owned by something. When you create a local string in the method, you have to transfer its ownership to the caller. But since you have to return &str, this is not an option.
The way around it would be to store the string in the struct itself. You can declare the enum value as B(usize, String), put the description there when you create it, and then return it with
B(_, ref descr) => descr
To be frank, description is not supposed to be a terribly detailed message, it just needs to give a general description of what kind of error this is, this is why it returns &str. I didn't see instances of writing any dynamic data into the description in the standard library, usually it's just a static string. Display implementation is a different matter though, in there you can be much more verbose.

Resources