Get data out of enum - rust

I have a function that received an enum with some data that I don't know inside. How can I retrieve its single values? For ex:
pub enum Type1{
Original {
A: u32,
B: u32,
},
Plus {
A: u32,
C: u32,
},
}
let bp = Type1::Original { A: 3, B: 4 };
// I have received bp but I want to work with bp.A
// How can I print it for example?
println!("{}", bp.A);
// Gives me the error: error[E0609]: no field `A` on type `Type1`

Use pattern matching:
let a = match bp {
Type1::Original { A, .. } => A,
Type1::Plus { A, .. } => A,
};
println!("{}", a);
Playground.

You can use if let:
if let Type1::Original { A, B } = bp {
println!("{}", A);
}
Playground

Related

Get struct from inside tuple variant [duplicate]

I wish that enums in Rust can be used like Haskell's productive type. I want to
access a field's value directly
assign a field's value directly or make a clone with the changing value.
Directly means that not using too long pattern matching code, but just could access like let a_size = a.size.
In Haskell:
data TypeAB = A {size::Int, name::String} | B {size::Int, switch::Bool} deriving Show
main = do
let a = A 1 "abc"
let b = B 1 True
print (size a) -- could access a field's value directly
print (name a) -- could access a field's value directly
print (switch b) -- could access a field's value directly
let aa = a{size=2} -- could make a clone directly with the changing value
print aa
I tried two styles of Rust enum definition like
Style A:
#[derive(Debug)]
enum EntryType {
A(TypeA),
B(TypeB),
}
#[derive(Debug)]
struct TypeA {
size: u32,
name: String,
}
#[derive(Debug)]
struct TypeB {
size: u32,
switch: bool,
}
fn main() {
let mut ta = TypeA {
size: 3,
name: "TAB".to_string(),
};
println!("{:?}", &ta);
ta.size = 2;
ta.name = "TCD".to_string();
println!("{:?}", &ta);
let mut ea = EntryType::A(TypeA {
size: 1,
name: "abc".to_string(),
});
let mut eb = EntryType::B(TypeB {
size: 1,
switch: true,
});
let vec_ab = vec![&ea, &eb];
println!("{:?}", &ea);
println!("{:?}", &eb);
println!("{:?}", &vec_ab);
// Want to do like `ta.size = 2` for ea
// Want to do like `ta.name = "bcd".to_string()` for ea
// Want to do like `tb.switch = false` for eb
// ????
println!("{:?}", &ea);
println!("{:?}", &eb);
println!("{:?}", &vec_ab);
}
Style B:
#[derive(Debug)]
enum TypeCD {
TypeC { size: u32, name: String },
TypeD { size: u32, switch: bool },
}
fn main() {
// NOTE: Rust requires representative struct name before each constructor
// TODO: Check constructor name can be duplicated
let mut c = TypeCD::TypeC {
size: 1,
name: "abc".to_string(),
};
let mut d = TypeCD::TypeD {
size: 1,
switch: true,
};
let vec_cd = vec![&c, &d];
println!("{:?}", &c);
println!("{:?}", &d);
println!("{:?}", &vec_cd);
// Can't access a field's value like
// let c_size = c.size
let c_size = c.size; // [ERROR]: No field `size` on `TypeCD`
let c_name = c.name; // [ERROR]: No field `name` on `TypeCD`
let d_switch = d.switch; // [ERROR]: No field `switch` on `TypeCD`
// Can't change a field's value like
// c.size = 2;
// c.name = "cde".to_string();
// d.switch = false;
println!("{:?}", &c);
println!("{:?}", &d);
println!("{:?}", &vec_cd);
}
I couldn't access/assign values directly in any style. Do I have to implement functions or a trait just to access a field's value? Is there some way of deriving things to help this situation?
What about style C:
#[derive(Debug)]
enum Color {
Green { name: String },
Blue { switch: bool },
}
#[derive(Debug)]
struct Something {
size: u32,
color: Color,
}
fn main() {
let c = Something {
size: 1,
color: Color::Green {
name: "green".to_string(),
},
};
let d = Something {
size: 2,
color: Color::Blue { switch: true },
};
let vec_cd = vec![&c, &d];
println!("{:?}", &c);
println!("{:?}", &d);
println!("{:?}", &vec_cd);
let _ = c.size;
}
If all variant have something in common, why separate them?
Of course, I need to access not common field too.
This would imply that Rust should define what to do when the actual type at runtime doesn't contain the field you required. So, I don't think Rust would add this one day.
You could do it yourself. It will require some lines of code, but that matches the behavior of your Haskell code. However, I don't think this is the best thing to do. Haskell is Haskell, I think you should code in Rust and not try to code Haskell by using Rust. That a general rule, some feature of Rust come directly from Haskell, but what you want here is very odd in my opinion for Rust code.
#[derive(Debug)]
enum Something {
A { size: u32, name: String },
B { size: u32, switch: bool },
}
impl Something {
fn size(&self) -> u32 {
match self {
Something::A { size, .. } => *size,
Something::B { size, .. } => *size,
}
}
fn name(&self) -> &String {
match self {
Something::A { name, .. } => name,
Something::B { .. } => panic!("Something::B doesn't have name field"),
}
}
fn switch(&self) -> bool {
match self {
Something::A { .. } => panic!("Something::A doesn't have switch field"),
Something::B { switch, .. } => *switch,
}
}
fn new_size(&self, size: u32) -> Something {
match self {
Something::A { name, .. } => Something::A {
size,
name: name.clone(),
},
Something::B { switch, .. } => Something::B {
size,
switch: *switch,
},
}
}
// etc...
}
fn main() {
let a = Something::A {
size: 1,
name: "Rust is not haskell".to_string(),
};
println!("{:?}", a.size());
println!("{:?}", a.name());
let b = Something::B {
size: 1,
switch: true,
};
println!("{:?}", b.switch());
let aa = a.new_size(2);
println!("{:?}", aa);
}
I think there is currently no built-in way of accessing size directly on the enum type. Until then, enum_dispatch or a macro-based solution may help you.

in expressions, `_` can only be used on the left-hand side of an assignment

I've the following code I implemented to return an error when a string doesn't contain a match to something like "Mark, 55" to return an error. The compiler complains about my code Err(error) => Err(ParsePersonError::ParseInt(_))
use std::num::ParseIntError;
use std::str::FromStr;
#[derive(Debug, PartialEq)]
struct Person {
name: String,
age: usize,
}
// We will use this error type for the `FromStr` implementation.
#[derive(Debug, PartialEq)]
enum ParsePersonError {
// Empty input string
Empty,
// Incorrect number of fields
BadLen,
// Empty name field
NoName,
// Wrapped error from parse::<usize>()
ParseInt(ParseIntError),
}
// My implementation
impl FromStr for Person {
type Err = ParsePersonError;
fn from_str(s: &str) -> Result<Person, Self::Err> {
if s.len() == 0 {
Err(ParsePersonError::Empty)
}
else {
let v: Vec<&str> = s.split(",").collect();
println!("{:?}",v);
if &v[0]== &""{
Err(ParsePersonError::NoName)
}
else if v.len()!=2 {
Err(ParsePersonError::BadLen)
}
else {
let num = match v[1].parse::<usize>() {
Ok(n) => {
let name = v[0].to_string();
let age = n;
return Ok(Person {name,age})
},
Err(error) => Err(ParsePersonError::ParseInt(_))
};
Err(ParsePersonError::ParseInt(_))
}
}
}
}
However, the compiler doesn't complain about the same code in my test case.
ParsePersonError::ParseInt(_))
#[test]
fn missing_name_and_age() {
assert!(matches!(
",".parse::<Person>(),
Err(ParsePersonError::NoName | ParsePersonError::ParseInt(_))
));
}
What am I missing?
If you de-sugar the match macro in test, it is something like
let result = ",".parse::<Person>();
let r = match result {
Err(ParsePersonError::NoName) => true,
Err(ParsePersonError::ParseInt(_)) => true,
_ => false,
};
assert!(r);
Essentially the '_' is to hold the value attached to ParseInt. Hence an assignment. This is what the compiler error message means.

Procedural macro for retrieving data from a nested struct by index

I am trying to write a rust derive macro for retrieving data from a nested struct by index. The struct only contains primitive types u8, i8, u16, i16, u32, i32, u64, i64, or other structs thereof. I have an Enum which encapsulates the leaf field data in a common type which I call an Item(). I want the macro to create a .get() implementation which returns an item based on a u16 index.
Here is the desired behavior.
#[derive(Debug, PartialEq, PartialOrd, Copy, Clone)]
pub enum Item {
U8(u8),
I8(i8),
U16(u16),
I16(i16),
U32(u32),
I32(i32),
U64(u64),
I64(i64),
}
struct NestedData {
a: u16,
b: i32,
}
#[derive(GetItem)]
struct Data {
a: i32,
b: u64,
c: NestedData,
}
let data = Data {
a: 42,
b: 1000,
c: NestedData { a: 500, b: -2 },
};
assert_eq!(data.get(0).unwrap(), Item::I32(42));
assert_eq!(data.get(1).unwrap(), Item::U64(1000));
assert_eq!(data.get(2).unwrap(), Item::U16(500));
assert_eq!(data.get(3).unwrap(), Item::I32(-2));
For this particular example, I want the macro to expand to the following...
impl Data {
pub fn get(&self, index: u16) -> Result<Item, Error> {
match index {
0 => Ok(Item::U16(self.a)),
1 => Ok(Item::I32(self.b)),
2 => Ok(Item::I32(self.c.a)),
3 => Ok(Item::U64(self.c.b)),
_ => Err(Error::BadIndex),
}
}
}
I have a working macro for a single layer struct, but I am not sure about how to modify it to support nested structs. Here is where I am at...
use proc_macro2::TokenStream;
use quote::quote;
use syn::{Data, DataStruct, DeriveInput, Fields, Type, TypePath};
pub fn impl_get_item(input: DeriveInput) -> syn::Result<TokenStream> {
let model_name = input.ident;
let fields = match input.data {
Data::Struct(DataStruct {
fields: Fields::Named(fields),
..
}) => fields.named,
_ => panic!("The GetItem derive can only be applied to structs"),
};
let mut matches = TokenStream::new();
let mut item_index: u16 = 0;
for field in fields {
let item_name = field.ident;
let item_type = field.ty;
let ts = match item_type {
Type::Path(TypePath { path, .. }) if path.is_ident("u8") => {
quote! {#item_index => Ok(Item::U8(self.#item_name)),}
}
Type::Path(TypePath { path, .. }) if path.is_ident("i8") => {
quote! {#item_index => Ok(Item::I8(self.#item_name)),}
}
Type::Path(TypePath { path, .. }) if path.is_ident("u16") => {
quote! {#item_index => Ok(Item::U16(self.#item_name)),}
}
Type::Path(TypePath { path, .. }) if path.is_ident("i16") => {
quote! {#item_index => Ok(Item::I16(self.#item_name)),}
}
Type::Path(TypePath { path, .. }) if path.is_ident("u32") => {
quote! {#item_index => Ok(Item::U32(self.#item_name)),}
}
Type::Path(TypePath { path, .. }) if path.is_ident("i32") => {
quote! {#item_index => Ok(Item::I32(self.#item_name)),}
}
Type::Path(TypePath { path, .. }) if path.is_ident("u64") => {
quote! {#item_index => Ok(Item::U64(self.#item_name)),}
}
Type::Path(TypePath { path, .. }) if path.is_ident("i64") => {
quote! {#item_index => Ok(Item::I64(self.#item_name)),}
}
_ => panic!("{:?} uses unsupported type {:?}", item_name, item_type),
};
matches.extend(ts);
item_index += 1;
}
let output = quote! {
#[automatically_derived]
impl #model_name {
pub fn get(&self, index: u16) -> Result<Item, Error> {
match index {
#matches
_ => Err(Error::BadIndex),
}
}
}
};
Ok(output)
}
I'm not going to give a complete answer as my proc-macro skills are non-existant, but I don't think the macro part is tricky once you've got the structure right.
The way I'd approach this is to define a trait that all the types will use. I'm going to call this Indexible which is probably bad. The point of the trait is to provide the get function and a count of all fields contained within this object.
trait Indexible {
fn nfields(&self) -> usize;
fn get(&self, idx:usize) -> Result<Item>;
}
I'm using fn nfields(&self) -> usize rather than fn nfields() -> usize as taking &self means I can use this on vectors and slices and probably some other types (It also makes the following code slightly neater).
Next you need to implement this trait for your base types:
impl Indexible for u8 {
fn nfields(&self) -> usize { 1 }
fn get(&self, idx:usize) -> Result<Item> { Ok(Item::U8(*self)) }
}
...
Generating all these is probably a good use for a macro (but the proc macro that you're talking about).
Next, you need to generate these for your desired types: My implementations look like this:
impl Indexible for NestedData {
fn nfields(&self) -> usize {
self.a.nfields() +
self.b.nfields()
}
fn get(&self, idx:usize) -> Result<Item> {
let idx = idx;
// member a
if idx < self.a.nfields() {
return self.a.get(idx)
}
let idx = idx - self.a.nfields();
// member b
if idx < self.b.nfields() {
return self.b.get(idx)
}
Err(())
}
}
impl Indexible for Data {
fn nfields(&self) -> usize {
self.a.nfields() +
self.b.nfields() +
self.c.nfields()
}
fn get(&self, idx:usize) -> Result<Item> {
let idx = idx;
if idx < self.a.nfields() {
return self.a.get(idx)
}
let idx = idx - self.a.nfields();
if idx < self.b.nfields() {
return self.b.get(idx)
}
let idx = idx - self.b.nfields();
if idx < self.c.nfields() {
return self.c.get(idx)
}
Err(())
}
}
You can see a complete running version in the playground.
These look like they can be easily generated by a macro.
If you want slightly better error messages on types that wont work, you should explicitly trea each member as an Indexible like this: (self.a as Indexible).get(..).
It might seem that this is not going to be particularly efficient, but the compiler is able to determine that most of these pieces are constant and inline them. For example, using rust 1.51 with -C opt-level=3, the following function
pub fn sum(data: &Data) -> usize {
let mut sum = 0;
for i in 0..data.nfields() {
sum += match data.get(i) {
Err(_) => panic!(),
Ok(Item::U8(v)) => v as usize,
Ok(Item::U16(v)) => v as usize,
Ok(Item::I32(v)) => v as usize,
Ok(Item::U64(v)) => v as usize,
_ => panic!(),
}
}
sum
}
compiles to just this
example::sum:
movsxd rax, dword ptr [rdi + 8]
movsxd rcx, dword ptr [rdi + 12]
movzx edx, word ptr [rdi + 16]
add rax, qword ptr [rdi]
add rax, rdx
add rax, rcx
ret
You can see this in the compiler explorer

Match an owned variable, then get access to the variable again

I want to do something along the lines of
enum A {
Type1 {
s: String
// ... some more fields
}
// ... some more variants
}
impl A {
fn consume(self) { println!("Consumed!"); }
}
fn fails() {
let b = A::Type1 { s: String::from("Arbitrary string") };
match b {
A::Type1 {
s, // (value moved here)
// ... more fields
} => {
let l = s.len(); // Something using the field from the enum
if l > 3 {
s.into_bytes(); // do something that requires ownership of s
} else {
b.consume(); // Value used here after move
}
}
// ... more cases
}
}
However, because I destructure b in the match case, I don't have access to it within the body of the match.
I can reconstruct b from the fields, but when b has lots of fields this is obviously not ideal. Is there any way to get around this issue without having to rebuild b?
No, you (almost literally) cannot have your cake and eat it too. Once you have destructured the value, the value no longer exists.
When non-lexical lifetimes happens, you can use a combination of NLL and match guards to prevent taking ownership in the first place:
#![feature(nll)]
enum A {
Type1 { s: String },
}
impl A {
fn consume(self) {
println!("Consumed!");
}
}
fn main() {
let b = A::Type1 {
s: String::from("Arbitrary string"),
};
match b {
A::Type1 { ref s } if s.len() <= 3 => {
b.consume();
}
A::Type1 { s } => {
s.into_bytes();
}
}
}

String return value does not get assigned within for-loop in Rust

Given this code-snippet:
pub fn verse(start_bottles: i32) -> String {
let mut song_template: String = "%1 of beer on the wall, %2 of beer.\n%3, %4 of beer on the wall.\n".to_string();
match start_bottles {
0 => lyrics_no_bottles(&mut song_template),
1 => lyrics_one_bottle(&mut song_template),
2 => lyrics_two_bottles(&mut song_template),
_ => lyrics_more_bottles(&mut song_template, start_bottles)
}
song_template
}
pub fn sing(first: i32, last: i32) -> String {
let mut song: String = "".to_string();
for num in (8..6).rev() {
song = verse(1);
}
song
}
As I output verse(1) it works fine - the tested string appears and is complete. But when I assign the result of verse(1) to the String binding song, song seems to be empty? I do not understand this behaviour.
Because that's not how ranges work; it's got nothing to do with the strings. If you run the following:
fn main() {
for i in 8..6 {
println!("a: {}", i);
}
for i in (8..6).rev() {
println!("b: {}", i);
}
for i in 6..8 {
println!("c: {}", i);
}
for i in (6..8).rev() {
println!("d: {}", i);
}
}
You get the following output:
c: 6
c: 7
d: 7
d: 6
Ranges only count up, they never count down. rev reverses the order of the sequence you give it; it doesn't turn an empty sequence into a non-empty one.

Resources