Rust macro generate enum from another enum - enums

I am trying to solve a problem with code duplication to make it less error-prone.
I'm working with different Data Sources each with some specific properties. Here is how my data model looks like:
mod sources {
pub struct Ds1;
pub struct Ds2;
//hundreds more
pub trait DsTypeTrait{
type Input;
type Output;
}
impl DsTypeTrait for Ds1 {
type Input = u32;
type Output = u32;
}
impl DsTypeTrait for Ds2 {
type Input = String;
type Output = String;
}
//etc...
enum DataSource{
Ds1(Ds1),
Ds2(Ds2),
//...
}
}
So any time somebody wants to add support for a new data source they need to add it to the enum DataSource. The PROBLEM with the solution is that if another module contains some custom data format for, e.g. DataSource communications it would be required to add the DataSource in the 2 places which is extremely error-prone. For example:
mod some_other {
use super::sources::*;
struct DataSourceDataInputOutput<Ds: DsTypeTrait>{
input: <Ds as DsTypeTrait>::Input,
output: <Ds as DsTypeTrait>::Output
}
enum DataSourceIO{
Ds1(DataSourceDataInputOutput<Ds1>),
Ds2(DataSourceDataInputOutput<Ds2>)
//Extremely easy to forget to add here
}
}
QUESTION: Given the enum DataSource is it possible to write a macro to generate enum DataSourceIO automatically and avoid modifying enum DataSourceIO manually each time a new DataSource is added?

It seems enum parsing is a complicated issue so it is reasonable to move the DataSource actual declaration in the macro itself. Here is what I came up with:
#[macro_use]
mod sources {
macro_rules! data_sources {
($name:ident, $type:tt) => {
enum $name{
Ds1($type<Ds1>),
Ds2($type<Ds2>)
}
}
}
//...
}
mod some_other {
struct DataSourceDataInputOutput<Ds: DsTypeTrait>{
input: <Ds as DsTypeTrait>::Input,
output: <Ds as DsTypeTrait>::Output
}
data_sources!(Test, DataSourceDataInputOutput);
fn test_fn() {
let ds1io: DataSourceDataInputOutput<Ds1> = DataSourceDataInputOutput{
input: 1,
output: 2
};
let test = DataSourceIO::Ds1(ds1io); //works ok
}
}

Related

Should I implement a trait also by value to allow for better performance?

Let's say I have the following enums :
enum SomeEnum {
V1(i32),
V2(f32),
V3(String),
V4(Vec<i32>),
}
#[derive(Debug)]
enum SomeEnumConversionError {
OutOfTypeRange,
StringParse,
TooManyElements,
}
And I want the following trait to work for both values and references of the first enum; So I implement it for the reference to save some time and a few lines of duplicate code (Considering I have to do almost the same for all other variants and the real enum I'm working with has about 30 variants) :
impl TryFrom<&SomeEnum> for i32 {
type Error = SomeEnumConversionError;
fn try_from(value: &SomeEnum) -> Result<Self, Self::Error> {
match value {
SomeEnum::V1(v) => Ok(*v),
SomeEnum::V2(v) => {
let i = *v as i32;
if (i as f32) == *v {
Ok(i)
} else {
Err(SomeEnumConversionError::OutOfTypeRange)
}
}
SomeEnum::V3(v) => v
.parse::<i32>()
.map_err(|_| SomeEnumConversionError::StringParse),
SomeEnum::V4(v) if v.len() == 1 => Ok(v[0]),
SomeEnum::V4(v) => Err(SomeEnumConversionError::TooManyElements),
}
}
}
impl TryFrom<SomeEnum> for i32 {
type Error = SomeEnumConversionError;
fn try_from(value: SomeEnum) -> Result<Self, Self::Error> {
(&value).try_into()
}
}
Now, if I have the following code as an example usage :
fn some_func() {
let a = SomeEnum::V4(vec![4i32]);
let b: Result<i32, _> = a.try_into();
println!("{}", b.unwrap());
}
The question is, how is this (performance-wise, considering the conversion might be called in a loop) compared to when I implement the trait for both value and reference type (rather than just calling it by reference from value implementation) ?
Does the compiler do some optimization magic here that will eventually make it behave as if the variable a has been actually moved to the function implementation calls ?
Or do I need to also implement the whole conversion function for the value type to ensure this ?
If there are better alternatives to the whole plan, I'm all ears.

Initialize data structure's field in Rust with another data structure

I have recently started to program in Rust and would like now to create a structure, which fields I would like to assign using other structures. In the code base, which I am working with, there is the following example:
pub struct StType1 {
f1: String,
f2: bool,
f3: usize,
}
pub struct StType2 {
// ...
field1: StType1,
// ...
}
impl StType2 {
// ...
pub fn build(mut self) {
// ...
let st = StType3 {
field1: self.field1,
};
// ...
}
pub fn field1(&self) -> &StType1 {
&self.field1
}
}
struct StType3 {
field1: StType1,
// ...
}
StType1 is the type of the field, which is contained in both structures StType2 and StType3. StType2 while running build() function creates StType3 and they should have the same value of field1.
In this code there is no move and seems no copy in this assignment field1: self.field1;. Why? What happens then?
I am asking because when I try to create a similar to StType3 - ObjType - structure outside this file:
let object1 = StType2::new(...);
let object2 = ObjType {
field1: object1.field1().clone(),
}
within my functions, I get a reference to StType1 after clone(), not an object, and when I am doing this:
let object1 = StType2::new(...);
let object2 = ObjType {
field1: *object1.field1().clone(),
}
I get an error Can't move:
move occurs because value has type
StType1, which does
not implement the Copy trait
I solve it by adding to the StType1 #[derive(Clone)]. But it worked before with StType2 without #[derive(Clone)]. Why should I add it now? Why there was no move happenning (or no clone required) for field1 when this StType3 was created inside StType2 and the field1 was initialized?
In this code there is no move and seems no copy in this assignment field1: self.field1. Why? What happens then?
This is incorrect – there is a move here, because Rust moves by default.
And this is possible because in the declaration above:
pub fn build(mut self) {
there is no & before the self, and so this build method takes self by-move.
In contrast, the method below:
pub fn field1(&self) -> &StType1 {
&self.field1
}
takes &self, i.e. a reference, and returns &StType1, another reference. So to get a StType1 to construct your object, you need to make it owned by calling .clone(). But that will only work if StType1 implements Clone, which is why you had to add #[derive(Clone)] to it.
Alternatively, you can change the field1() method to take self by-move as well:
pub fn field1(self) -> StType1 {
self.field1
}
The disadvantage of this approach is that since it consumes self, i.e. the entire struct, you won't be able to access any of the other fields after it's called.

How to parameterize enumerator in rust?

I'm very new to Rust and faced the following simple problem
I have the following 2 enums:
enum SourceType{
File,
Network
}
enum SourceProperties{
FileProperties {
file_path: String
},
NetworkProperties {
ip: String
}
}
Now I'd like to have HashMap<SourceType, SourceProperties>, but in such an implementation there is a potential to have mapping File -> NetworkProperties which is not what's expected.
I was thinking about to parameterize enum SourceProperties<T> with SourceType somehow, but it does not seem to be possible. Is there a way to provide such typesafety guarantees?
UPD: The intention of having the enum SourceType is that the actual SourceType is a user input which will be decoded as a String value ("File", "Network"). So the workflow would look like this
"File" -> SourceType::File -> SourceProperties::NetworkProperties
You can simple use a hash set and an enum that encapsulates the properties, for latter matching of them:
use std::collections::HashSet;
#[derive(PartialEq, Eq, Hash)]
struct FileProperties {
file_path: String
}
#[derive(PartialEq, Eq, Hash)]
struct NetworkProperties {
ip: String
}
#[derive(PartialEq, Eq, Hash)]
enum Source {
File(FileProperties),
Network(NetworkProperties)
}
fn main() {
let mut set : HashSet<Source> = HashSet::new();
set.insert(Source::File(FileProperties{file_path: "foo.bar".to_string()}));
for e in set {
match e {
Source::File(properties) => { println!("{}", properties.file_path);}
Source::Network(properties) => { println!("{}", properties.ip);}
}
}
}
Playground

How can I force a struct's field to always be immutable in Rust?

In Rust, you don't specify mutability inside a struct, but it is inherited from the variable binding. That's great, but is it possible to force a field to be always immutable, even when the root is mutable?
Something like this hypothetical syntax:
struct A {
immut s: Shape, // immutable by design
bla: Bla, // this field inheriting (im)mutability
}
let mut a = make_a();
a.s = x/*...*/; // illegal
This would help to maintain nice semantic restrictions in a program, just like Java's final does (in a very limited way).
Also, we could imagine this kind of struct having some non-owning references to internal immutable data, taking advantage of this immutability...
It's impossible to have immutability of a single field. That was an option in an ancient version of Rust (think before 0.8), but it was dropped because the rules confused a LOT of people. How was it confusing, you might ask? Think about it like this: if a field is declared mutable and struct is declared mutable and the reference used was an immutable reference (&) then the field is _______.
The best, as Lily Ballard noted, is that you can declare your Shape field as private and make a getter method using impl A {...}.
mod inner {
pub struct A {
s: i32, // can't be seen outside of module
pub bla: i32,
}
impl A {
pub fn new() -> Self {
Self { s: 0, bla: 42 }
}
pub fn get_s(&self) -> i32 {
self.s
}
}
}
let mut a = inner::A::new();
a.s = 42; // illegal
println!("{}", a.s); // also illegal
println!("{}", a.get_s()); // could be made to serve as a read-only method
error[E0616]: field `s` of struct `main::inner::A` is private
--> src/main.rs:20:5
|
20 | a.s = 42; // illegal
| ^^^
error[E0616]: field `s` of struct `main::inner::A` is private
--> src/main.rs:21:20
|
21 | println!("{}", a.s); // also illegal
| ^^^
There is proposition that might drop notions of mutability and immutability completely (you can't say a struct never changes). See Niko's explanation for that change.
You can create a struct and only implement the Deref trait for it. Without the DerefMut trait it won't be possible for contained values to be mutated.
https://doc.rust-lang.org/std/ops/trait.Deref.html
This way the compiler will make the member usable as if it's not wrapped in another struct, no need for any written getter method call.
use std::ops::Deref;
/// A container for values that can only be deref'd immutably.
struct Immutable<T> {
value: T,
}
impl<T> Immutable<T> {
pub fn new(value: T) -> Self {
Immutable { value }
}
}
impl<T> Deref for Immutable<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.value
}
}
struct Foo {
bar: Immutable<Vec<u8>>,
baz: usize,
}
impl Foo {
pub fn new(vec: Vec<u8>) -> Self {
Foo {
bar: Immutable::new(vec),
baz: 1337,
}
}
pub fn mutate(&mut self) {
self.bar.push(0); // This will cause a compiler error
}
}
|
| self.bar.push(0);
| ^^^^^^^^ cannot borrow as mutable
|
= help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `runnable::immutable::Immutable<std::vec::Vec<u8>>`
You can't force immutability on a field. How would the struct mutate its own value when necessary?
What you can do is make the field private and expose a getter method to return a reference to it (or to copy/clone the value).
A Solution could be to have a more general approach:
pub struct Immutable<T> {
value : T ,
}
impl<T> Immutable<T> {
pub fn new(value : T) -> Immutable<T> {
Immutable { value : value }
}
pub fn get( &self) -> &T { &self.value }
}
Now it is possible to use the Immutable struct in every case for every other type.
Given this into a module avoids changing the content of the Immutable object. It is still possible to change the variable which holds the Immutable object itself by overwriting it by a new Object but you should notice it by the Immutable::new statement and so you can avoid using it.
In many cases an associated constant achieves the desired behaviour:
struct Foo { blah: Blah }
impl Foo {
const S: Shape = Shape { x: 1, y: 1 };
}
Of course, constants cannot be set on creation, they are set at compile-time. Making the field private as explained in other answers will work if dynamicism is required.
Probably the easiest way to achieve this today is by using the readonly crate from prolific developer David Tolnay (author of serde and many others). From the github documentation:
This crate provides an attribute macro to expose struct fields that are readable and writable from within the same module but readable only outside the module.
#[readonly::make]
pub struct S {
// This field can be read (but not written) by super.
#[readonly]
pub(super) readable: i32,
// This field can be neither read nor written by other modules.
private: i32,
}

In Rust, is there a way to iterate through the values of an enum?

I come from a Java background and I might have something like enum Direction { NORTH, SOUTH, EAST, WEST} and I could do something with each of the values in turn with the enhanced for loop like:
for(Direction dir : Direction.values()) {
//do something with dir
}
I would like to do a similar thing with Rust enums.
You can use the strum crate to easily iterate through the values of an enum.
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);
}
}
Output:
NORTH
SOUTH
EAST
WEST
If the enum is C-like (as in your example), then you can create a static array of each of the variants and return an iterator of references to them:
use self::Direction::*;
use std::slice::Iter;
#[derive(Debug)]
pub enum Direction {
North,
South,
East,
West,
}
impl Direction {
pub fn iterator() -> Iter<'static, Direction> {
static DIRECTIONS: [Direction; 4] = [North, South, East, West];
DIRECTIONS.iter()
}
}
fn main() {
for dir in Direction::iterator() {
println!("{:?}", dir);
}
}
If you make the enum implement Copy, you can use Iterator::copied and return impl Trait to have an iterator of values:
impl Direction {
pub fn iterator() -> impl Iterator<Item = Direction> {
[North, South, East, West].iter().copied()
}
}
See also:
What is the correct way to return an Iterator (or any other trait)?
Why can I return a reference to a local literal but not a variable?
No, there is none. I think that is because enums in Rust are much more powerful than in Java - they are in fact full-fledged algebraic data types. For example, how would you expect to iterate over values of this enum:
enum Option<T> {
None,
Some(T)
}
?
Its second member, Some, is not a static constant - you use it to create values of Option<T>:
let x = Some(1);
let y = Some("abc");
So there is no sane way you can iterate over values of any enum.
Of course, I think, it would be possible to add special support for static enums (i.e. enums with only static items) into the compiler, so it would generate some function which return values of the enum or a static vector with them, but I believe that additional complexity in the compiler is just not worth it.
If you really want this functionality, you could write a custom syntax extension (see this issue). This extension should receive a list of identifiers and output an enum and a static constant vector with these identifiers as a content. A regular macro would also work to some extent, but as far as I remember you cannot transcript macro arguments with multiplicity twice, so you'll have to write enum elements twice manually, which is not convenient.
Also this issue may be of some interest: #5417
And of course you can always write code which returns a list of enum elements by hand.
I implemented basic functionality in the crate plain_enum.
It can be used to declare a C-like enum as follows:
#[macro_use]
extern crate plain_enum;
plain_enum_mod!(module_for_enum, EnumName {
EnumVal1,
EnumVal2,
EnumVal3,
});
And will then allow you to do things like the following:
for value in EnumName::values() {
// do things with value
}
let enummap = EnumName::map_from_fn(|value| {
convert_enum_value_to_mapped_value(value)
})
You can use an associated constant:
impl Direction {
const VALUES: [Self; 4] = [Self::NORTH, Self::SOUTH, Self::EAST, Self::WEST];
}
fn main() {
for direction in Direction::VALUES.iter().copied() {
todo!();
}
}
If you do not want to import a third-party crate, you can make your own macro to do so. Here is how I achieved it (there are probably ways to improve this):
macro_rules! iterable_enum {
($visibility:vis, $name:ident, $($member:tt),*) => {
$visibility enum $name {$($member),*}
impl $name {
fn iterate() -> Vec<$name> {
vec![$($name::$member,)*]
}
}
};
($name:ident, $($member:tt),*) => {
iterable_enum!(, $name, $($member),*)
};
}
And then you can do:
iterable_enum!(pub, EnumName, Value1, Value2, Value3);
fn main() {
for member in EnumName::iterate() {
// ...
}
}
This implementation is limited to simple enums. Consider the following enum, how would you iterate over it?:
enum MoreComplexEnum<T1, T2> {
One(T1),
Two(T2),
Other,
Both(T1, T2),
Error(String),
}
Because of the power of enums in Rust, it can be difficult to implement a perfectly iterable enum, since they are not like the simple enums you have in C or Java.
The enum-iterator crate helps iterating enumerators.
Here's my take on #Ahmed Merez's answer that:
doesn't allocate on the heap
is const
accepts (almost) an actual enum as input (requires vis cause Rust doesn't seem to like an empty visibility parameter with an error of error: repetition matches empty token tree)
including:
derive input
attribute properties
#[macro_export]
macro_rules! count {
() => (0usize);
( $x:tt $($xs:tt)* ) => (1usize + $crate::count!($($xs)*));
}
/// https://stackoverflow.com/a/64678145/10854888
macro_rules! iterable_enum {
($(#[$derives:meta])* $(vis $visibility:vis)? enum $name:ident { $($(#[$nested_meta:meta])* $member:ident),* }) => {
const count_members:usize = $crate::count!($($member)*);
$(#[$derives])*
$($visibility)? enum $name {
$($(#[$nested_meta])* $member),*
}
impl $name {
pub const fn iter() -> [$name; count_members] {
[$($name::$member,)*]
}
}
};
}
fn main() {
iterable_enum! {
#[derive(Debug, serde::Deserialize)]
vis pub(crate) enum X {
#[serde(rename="a")]
A,
B
}
}
for x in X::iter() {
dbg!(x);
}
}
My variation for #koral's answer
doesn't require vis
implements into_iter which returns an actual iterator instead of an array
macro_rules! iterable_enum {(
$(#[$derives:meta])*
$pub:vis enum $name:ident {
$(
$(#[$nested_meta:meta])*
$member:ident,
)*
}) => {
const _MEMBERS_COUNT:usize = iterable_enum!(#count $($member)*);
$(#[$derives])*
$pub enum $name {
$($(#[$nested_meta])* $member),*
}
impl $name {
pub fn into_iter() -> std::array::IntoIter<$name, _MEMBERS_COUNT> {
[$($name::$member,)*].into_iter()
}
}
};
(#count) => (0usize);
(#count $x:tt $($xs:tt)* ) => (1usize + iterable_enum!(#count $($xs)*));
}
fn main() {
iterable_enum! {
#[derive(Debug, serde::Deserialize)]
pub enum X {
#[serde(rename="a")]
A,
B,
}}
X::into_iter().fold(...);
}

Resources