Difference between match binding and match destructuring? - rust

Recently I was reading about the match keyword in the Rust Book. What confused me was the difference between Binding and Destructuring. In my understanding, both of these provide a way to access variables in an expression. Binding can specify a range matching, but you can achieve it with Destructuring and Guards. So can someone show some cases that only Binding can do or explain the real difference between these two concepts?

Here you can see one scenario when a binding is needed because destructuring doesn't satisfy our current need. If we simply destructure the struct we get access to the inner field of the struct. This means that the values used on the right hand side in the match arm won't have access to the methods defined on the struct.
In my example I also match against a specific value of ex.value, this is of course not necessary and can be done with a guard instead, this way is however more concise if the condition isn't very complex.
struct Example {
some_value: i32,
some_other_value: String
}
impl Example {
pub fn some_fn(&mut self) {}
}
fn main() {
let ex = Example { some_value: 42, some_other_value: "Foobar".to_string() };
match ex {
mut new_ex # Example { some_value: 43, .. } => new_ex.some_fn(),
Example { some_value: first, some_other_value: second } => println!("first value: {}\nSecond value: {}", first, second),
}
}

Related

Rust safe const iterable associative array

I would like to create a structure that's something like a compile-time immutable map with safely checked keys at compile-time. More generally, I would like an iterable associative array with safe key access.
My first attempt at this was using a const HashMap (such as described here) but then the keys are not safely accessible:
use phf::{phf_map};
static COUNTRIES: phf::Map<&'static str, &'static str> = phf_map! {
"US" => "United States",
"UK" => "United Kingdom",
};
COUNTRIES.get("EU") // no compile-time error
Another option I considered was using an enumerable enum with the strum crate as described here:
use strum::IntoEnumIterator; // 0.17.1
use strum_macros::EnumIter; // 0.17.1
#[derive(Debug, EnumIter)]
enum Direction {
NORTH,
SOUTH,
EAST,
WEST,
}
fn main() {
for direction in Direction::iter() {
println!("{:?}", direction);
}
}
This works, except that enum values in rust can only be integers. To assign a different value would require something like implementing a value() function for the enum with a match statement, (such as what's described here), however this means that any time the developer decides to append a new item, the value function must be updated as well, and rewriting the enum name in two places every time isn't ideal.
My last attempt was to use consts in an impl, like so:
struct MyType {
value: &'static str
}
impl MyType {
const ONE: MyType = MyType { value: "one" };
const TWO: MyType = MyType { value: "two" };
}
This allows single-write implementations and the objects are safely-accessible compile-time constants, however there's no way that I've found to iterate over them (as expressed by work-arounds here) (although this may be possible with some kind of procedural macro).
I'm coming from a lot of TypeScript where this kind of task is very simple:
const values = {
one: "one",
two: "two" // easy property addition
}
values.three; // COMPILE-TIME error
Object.keys(values).forEach(key => {...}) // iteration
Or even in Java where this can be done simply with enums with properties.
I'm aware this smells a bit like an XY problem, but I don't really think it's an absurd thing to ask generally for a safe, iterable, compile-time immutable constant associative array (boy is it a mouthful though). Is this pattern possible in Rust? The fact that I can't find anything on it and that it seems so difficult leads me to believe what I'm doing isn't the best practice for Rust code. In that case, what are the alternatives? If this is a bad design pattern for Rust, what would a good substitute be?
#JakubDóka How would I implement it? I did some looking at procedural macros and couldn't seem to understand how to implement such a macro.
macro_rules! decl_named_iterable_enum {
(
// notice how we format input as it should be inputted (good practice)
// here is the indentifier bound to $name variable, when we later mention it
// it will be replaced with the passed value
$name:ident {
// the `$(...)*` matches 0-infinity of consecutive `...`
// the `$(...)?` matches 0-1 of `...`
$($variant:ident $(= $repr:literal)?,)*
}
) => {
#[derive(Clone, Copy)]
enum $name {
// We use the metavar same way we bind it,
// just ommitting its token type
$($variant),*
// ^ this will insert `,` between the variants
}
impl $name {
// same story just with additional tokens
pub const VARIANTS: &[Self] = &[$(Self::$variant),*];
pub const fn name(self) -> &'static str {
match self {
$(
// see comments on other macro branches, this si a
// common way to handle optional patterns
Self::$variant => decl_named_iterable_enum!(#repr $variant $($repr)?),
)*
}
}
}
};
// this branch will match if literal is present
// in this case we just ignore the name
(#repr $name:ident $repr:literal) => {
$repr
};
// fallback for no literal provided,
// we stringify the name of variant
(#repr $name:ident) => {
stringify!($name)
};
}
// this is how you use the macro, similar to typescript
decl_named_iterable_enum! {
MyEnum {
Variant,
Short = "Long",
}
}
// some example code collecting names of variants
fn main() {
let name_list = MyEnum::VARIANTS
.iter()
.map(|v| v.name())
.collect::<Vec<_>>();
println!("{name_list:?}");
}
// Exercise for you:
// 1. replace `=` for name override with `:`
// 2. add a static `&[&str]` names accessed by `MyEnum::VARIANT_NAMES`

How to define one function for two types in Rust?

I am writing a Rust application. I'd like to have a method to display the text whether it is a string or number. Furthermore, I came up with the following solution, but it is duplicating the code. Is there a better way to do it in Rust?
Notice: I am not looking for a built-in function to print variables. It's just an example. I am looking for a way to implement the same feature for two types.
trait Display {
fn display(self);
}
impl Display for String {
fn display(self) -> () {
println!("You wrote: {}", self);
}
}
impl Display for i32 {
fn display(self) -> () {
println!("You wrote: {}", self);
}
}
fn main() {
let name: String = String::from("Tom Smykowski");
name.display();
let age: i32 = 22;
age.display();
}
You came close. But there is already a trait for converting things to strings - std::fmt::Display (and the automatically-implemented ToString) - so you don't need to have your own trait:
fn display<T: std::fmt::Display>(v: T) {
println!("You wrote: {v}");
}
fn main() {
let name: String = String::from("Tom Smykowski");
display(name);
let age: i32 = 22;
display(age);
}
Even if you don't need to display the types but do something else with them, we can take the idea from Display - instead of defining the whole functionality, define only the pieces that are different. For example, you can create a trait to convert the numbers to strings (or the opposite), or just have functions for each different piece - for example, printing itself without "You wrote: ".
I came up with the following solution, but it is duplicating the code. Is there a better way to do it in Rust?
Add a simple declarative macro on top, that is very common in the stdlib and all. e.g.
macro_rules! impl_display {
($t:ty) => {
impl Display for $t {
fn display(self) {
println!("You wrote {self}");
}
}
}
}
impl_display!(String);
impl_display!(i32);
impl_display!(i64);
impl_display!(f32);
Although:
usually the implementations would be different, though not always e.g. implementing an operation on all numeric types, or all unsigned numbers, that's one of the most common context you'll see it in the stdlib: the stdlib has no numeric trait but methods are usually implemented on all numeric types, so there's a handful of macros used for all of them, and when new methods are added they're just added to the relevant macro
here you're already relying on the existence and implementation of std::fmt::Display so you should just use that, your trait is not really useful

Rust treat two different structs as one

I have two different structs with similar functions. Suppose that the program choose which struct to take from the user input.
I want to write something like this
fn main() {
...
let obj = if a == first {
first_object //struct First
} else {
second_object//struct Second
};
obj.read();
obj.write();
obj.some_another_method();
}
I have tried to make an enumeration
pub enum ObjectKind {
FirstObject(First),
SecondObject(Second)
}
But I cannot use methods in this case
let something = ObjectKind::FirstObject(first_object);
something.read()
//no method named `read` found for enum `structs::ObjectKind` in the current scope
//method not found in `structs::ObjectKind`
But I cannot use methods in this case
An enum is a proper type in and of itself, it's not a "union" of existing types. You can just define the methods on the enum to forward to the relevant object e.g.
impl ObjectKind {
fn read(&self) {
match self {
FirstObject(f) => f.read()
SecondObject(s) => s.read()
}
}
}
as it would probably be a bit repetitive, you can use a macro to make the forwarding easier.
Alternatively, you might be able to define a trait for the common behaviour and use a trait object to dynamically dispatch between the two, but that can be somewhat restrictive.

Convenient 'Option<Box<Any>>' access when success is assured?

When writing callbacks for generic interfaces, it can be useful for them to define their own local data which they are responsible for creating and accessing.
In C I would just use a void pointer, C-like example:
struct SomeTool {
int type;
void *custom_data;
};
void invoke(SomeTool *tool) {
StructOnlyForThisTool *data = malloc(sizeof(*data));
/* ... fill in the data ... */
tool.custom_data = custom_data;
}
void execute(SomeTool *tool) {
StructOnlyForThisTool *data = tool.custom_data;
if (data.foo_bar) { /* do something */ }
}
When writing something similar in Rust, replacing void * with Option<Box<Any>>, however I'm finding that accessing the data is unreasonably verbose, eg:
struct SomeTool {
type: i32,
custom_data: Option<Box<Any>>,
};
fn invoke(tool: &mut SomeTool) {
let data = StructOnlyForThisTool { /* my custom data */ }
/* ... fill in the data ... */
tool.custom_data = Some(Box::new(custom_data));
}
fn execute(tool: &mut SomeTool) {
let data = tool.custom_data.as_ref().unwrap().downcast_ref::<StructOnlyForThisTool>().unwrap();
if data.foo_bar { /* do something */ }
}
There is one line here which I'd like to be able to write in a more compact way:
tool.custom_data.as_ref().unwrap().downcast_ref::<StructOnlyForThisTool>().unwrap()
tool.custom_data.as_ref().unwrap().downcast_mut::<StructOnlyForThisTool>().unwrap()
While each method makes sense on its own, in practice it's not something I'd want to write throughout a code-base, and not something I'm going to want to type out often or remember easily.
By convention, the uses of unwrap here aren't dangerous because:
While only some tools define custom data, the ones that do always define it.
When the data is set, by convention the tool only ever sets its own data. So there is no chance of having the wrong data.
Any time these conventions aren't followed, its a bug and should panic.
Given these conventions, and assuming accessing custom-data from a tool is something that's done often - what would be a good way to simplify this expression?
Some possible options:
Remove the Option, just use Box<Any> with Box::new(()) representing None so access can be simplified a little.
Use a macro or function to hide verbosity - passing in the Option<Box<Any>>: will work of course, but prefer not - would use as a last resort.
Add a trait to Option<Box<Any>> which exposes a method such as tool.custom_data.unwrap_box::<StructOnlyForThisTool>() with matching unwrap_box_mut.
Update 1): since asking this question a point I didn't include seems relevant.
There may be multiple callback functions like execute which must all be able to access the custom_data. At the time I didn't think this was important to point out.
Update 2): Wrapping this in a function which takes tool isn't practical, since the borrow checker then prevents further access to members of tool until the cast variable goes out of scope, I found the only reliable way to do this was to write a macro.
If the implementation really only has a single method with a name like execute, that is a strong indication to consider using a closure to capture the implementation data. SomeTool can incorporate an arbitrary callable in a type-erased manner using a boxed FnMut, as shown in this answer. execute() then boils down to invoking the closure stored in the struct field implementation closure using (self.impl_)(). For a more general approach, that will also work when you have more methods on the implementation, read on.
An idiomatic and type-safe equivalent of the type+dataptr C pattern is to store the implementation type and pointer to data together as a trait object. The SomeTool struct can contain a single field, a boxed SomeToolImpl trait object, where the trait specifies tool-specific methods such as execute. This has the following characteristics:
You no longer need an explicit type field because the run-time type information is incorporated in the trait object.
Each tool's implementation of the trait methods can access its own data in a type-safe manner without casts or unwraps. This is because the trait object's vtable automatically invokes the correct function for the correct trait implementation, and it is a compile-time error to try to invoke a different one.
The "fat pointer" representation of the trait object has the same performance characteristics as the type+dataptr pair - for example, the size of SomeTool will be two pointers, and accessing the implementation data will still involve a single pointer dereference.
Here is an example implementation:
struct SomeTool {
impl_: Box<SomeToolImpl>,
}
impl SomeTool {
fn execute(&mut self) {
self.impl_.execute();
}
}
trait SomeToolImpl {
fn execute(&mut self);
}
struct SpecificTool1 {
foo_bar: bool
}
impl SpecificTool1 {
pub fn new(foo_bar: bool) -> SomeTool {
let my_data = SpecificTool1 { foo_bar: foo_bar };
SomeTool { impl_: Box::new(my_data) }
}
}
impl SomeToolImpl for SpecificTool1 {
fn execute(&mut self) {
println!("I am {}", self.foo_bar);
}
}
struct SpecificTool2 {
num: u64
}
impl SpecificTool2 {
pub fn new(num: u64) -> SomeTool {
let my_data = SpecificTool2 { num: num };
SomeTool { impl_: Box::new(my_data) }
}
}
impl SomeToolImpl for SpecificTool2 {
fn execute(&mut self) {
println!("I am {}", self.num);
}
}
pub fn main() {
let mut tool1: SomeTool = SpecificTool1::new(true);
let mut tool2: SomeTool = SpecificTool2::new(42);
tool1.execute();
tool2.execute();
}
Note that, in this design, it doesn't make sense to make implementation an Option because we always associate the tool type with the implementation. While it is perfectly valid to have an implementation without data, it must always have a type associated with it.

Accessing tuple from within an enum

I have a Rust enum defined like this
enum MyFirstEnum {
TupleType(f32, i8, String),
StuctType {varone: i32, vartwo: f64},
NewTypeTuple(i32),
SomeVarName
}
I have the following code:
let mfe: MyFirstEnum = MyFirstEnum::TupleType(3.14, 1, "Hello".to_string());
I'm following the Rust documentation and this looks fine. I don't need to define everything in the enum, but how would I go about accessing the mid element in the enum tuple?
mfe.TupleType.1 and mfe.1 don't work when I add them to a println!
I know Rust provides the facility to do pattern matching to obtain the value, but if I changed the code to define the other variants within the enum, the code to output a particular variant would quickly become a mess.
Is there a simple way to output the variant of the tuple (or any other variant) in the enum?
This is a common misconception: enum variants are not their own types (at least in Rust 1.9). Therefore when you create a variable like this:
let value = MyFirstEnum::TupleType(3.14, 1, "Hello".to_string());
The fact that it's a specific variant is immediately "lost". You will need to pattern match to prevent accessing the enum as the wrong variant. You may prefer to use an if let statement instead of a match:
if let MyFirstEnum::TupleType(f, i, s) = value {
// Values available here
println!("f: {:?}", f);
}
Example solution:
enum MyFirstEnum {
TupleType(f32, i8, String),
// StuctType { varone: i32, vartwo: f64 },
// NewTypeTuple(i32),
// SomeVarName,
}
fn main() {
let mfe: MyFirstEnum = MyFirstEnum::TupleType(3.14, 1, "Hello".to_string());
let MyFirstEnum::TupleType(value, id, text) = &mfe;
println!("[{}; {}; {}]", value, id, text);
//or
match &mfe {
MyFirstEnum::TupleType(value, id, text) => {
println!("[{}; {}; {}]", value, id, text);
}
// _ => {}
}
}
Playground link

Resources