Cast a Rust enum to a sub-enum - enums

I'm creating subsets of std::sync::atomic::Ordering:
use std::sync::atomic::Ordering;
pub enum StoreOrdering {
Relaxed,
Release,
SeqCst
}
impl Into<Ordering> for StoreOrdering {
fn into(self) -> Ordering {
match self {
Self::Relaxed => Ordering::Relaxed,
Self::Release => Ordering::Release,
Self::SeqCst => Ordering::SeqCst
}
}
}
impl std::convert::TryFrom<Ordering> for StoreOrdering {
type Error = (); // HACK
fn try_from(ord: Ordering) -> Result<Self, Self::Error> {
match ord {
Ordering::Relaxed => Ok(Self::Relaxed),
Ordering::Release => Ok(Self::Release),
Ordering::SeqCst => Ok(Self::SeqCst),
_ => Err(())
}
}
}
enum LoadOrdering {
Acquire,
Relaxed,
SeqCst
}
// ???
As you can see, now I need to write those two impls with matches again for StoreOrdering <-> LoadOrdering and maybe even for StoreOrdering <-> LoadOrdering - as well as for any enum subset. How to avoid such boilerplate?

Rust doesn't support duck typing like C++ does with templates. The only functionality that generics can access is determined by the trait bounds.
So any duck-type-like behaviour must be done with macros.
For this, you could use the given macro below.
It only works for simple C-style macros. It creates the enum and auto-generates the conversions to the given super-enum.
use std::sync::atomic::Ordering;
use std::convert::TryInto;
// Create the store ordering
sub_enum!(StoreOrdering of Ordering {
Relaxed,
Release,
SeqCst
});
// Create the load ordering
sub_enum!(LoadOrdering of Ordering {
Acquire,
Relaxed,
SeqCst
});
#[macro_export]
macro_rules! sub_enum {
($sub_enum_name:ident of $super_enum_name:ty {
$($variant:ident),* $(,)?
}) => {
pub enum $sub_enum_name {
$($variant,)*
}
impl From<$sub_enum_name> for $super_enum_name {
fn from(val: $sub_enum_name) -> $super_enum_name {
match val {
$(<$sub_enum_name>::$variant => <$super_enum_name>::$variant,)*
}
}
}
impl std::convert::TryFrom<$super_enum_name> for $sub_enum_name {
type Error = ();
fn try_from(val: $super_enum_name) -> Result<Self, Self::Error> {
match val {
$(<$super_enum_name>::$variant => Ok(Self::$variant),)*
_ => Err(())
}
}
}
}
}
fn main() {
let store = StoreOrdering::SeqCst;
let general: Ordering = store.into();
let load: LoadOrdering = general.try_into().unwrap();
}
Playground link
A lot could be improved still, of course.
However, this should do for your problem right now.

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.

Prae:Wrapper: need to use iter_mut of interior Vec but Prae:Wrapper only provides immutable access

I'm using the prae crate for validation and the following function gives me errors:
fn advance_rotors(&mut self) {
self.rotors.get()[0].rotate();
let mut iterhandle = self.rotors.iter_mut().peekable(); // Error at iter_mut() #0599
while let Some(el) = iterhandle.next() {
match iterhandle.peek_mut() {
Some(next_rotor) => match el.should_advance_next() {
true => {
next_rotor.rotate(); // This line requires mutable access to next_rotor
}
false => (),
},
None => (),
}
}
}
and the definition of my struct here:
pub struct Enigma {
reflector: Reflector,
rotors: RotorConfig, // Only mutable via getter and setter functions
}
the struct of interest here is RotorConfig which is generated using the define! macro from prae. Here's the code:
prae::define! {
#[derive(Debug)]
RotorConfig: Vec<Rotor>; // I need to be able to call the rotate method of each rotor in this vec. This requires mutability
validate(RotorConfigError) |config| {
match config.len(){
3..=4 => (),
_ => return Err(RotorConfigError::Size)
}
match config.iter().unique().count(){
3..=4 =>(),
_ => return Err(RotorConfigError::Duplicate)
}
Ok(())
};
}
the issue stems from the fact that prae only allows for immutable access to the internal representation via getter and setter functions so as to ensure the validity of the values inside. As you can see in my advance_rotors function I wrote before implementing validation I'm getting an error because I need to call rotor.rotate mutably. I'm at a loss as to how to accomplish this
After posting this I realized that I can simply provide interior mutability by using the following impl block
impl RotorConfig{
fn advance_rotors(&mut self)
{
self.0[0].rotate();
let mut iterhandle = self.0.iter_mut().peekable();
while let Some(el) = iterhandle.next() {
match iterhandle.peek_mut() {
Some(next_rotor) => match el.should_advance_next() {
true => {
next_rotor.rotate();
}
false => (),
},
None => (),
}
}
}
}
As you can see the function largely remains unchanged except that we replace self.rotors with self.0

egui combobox vector for selected

I am trying to use a vector instead of the enum specified in the docs but I have no clue how to implement the selected part. My current code is
egui::ComboBox::from_label("Take your pick")
.selected_text(format!("{}", self.radio[0]))
.show_ui(ui, |ui| {
for i in 0..self.radio.len() {
ui.selectable_value(&mut &self.radio, &self.radio, &self.radio[i]);
}
});
can anyone give me an idea. I do not mind using enum but I do not know how many things will be in it.
I stumbled on the same problem, and I have found a solution. The solution is to add a new Variable to the self. Here is a basic example on the Enum select and the Vector select. Hope this helps out anyone who has run into the same problem.
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use eframe::egui;
fn main() {
let options = eframe::NativeOptions::default();
eframe::run_native(
"Test Select with Enum and Vector",
options,
Box::new(|_cc| Box::new(MyApp::default())),
);
}
#[derive(PartialEq)]
#[derive(Debug)]
enum OS_Select {
First,
Second,
Third,
}
struct MyApp {
selected: usize,
radio: OS_Select,
selector_vec: Vec<String>,
}
impl Default for MyApp {
fn default() -> Self {
Self {
selected: 0,
radio: OS_Select::First,
selector_vec: get_vec(),
}
}
}
fn get_vec() -> Vec<String> {
let vecs = [
"1".to_string(),
"2".to_string(),
"3".to_string(),
].to_vec();
return vecs;
}
impl eframe::App for MyApp {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
egui::CentralPanel::default().show(ctx, |ui| {
ui.heading("Select with enum");
ui.horizontal(|ui| {
ui.radio_value(&mut self.radio, OS_Select::First, "First");
ui.radio_value(&mut self.radio, OS_Select::Second, "Second");
ui.radio_value(&mut self.radio, OS_Select::Third, "Third");
});
ui.end_row();
ui.heading("Select with Vectors");
egui::ComboBox::from_label("Take your pick")
.selected_text(format!("{}", &self.selector_vec[self.selected]))
.show_ui(ui, |ui| {
for i in 0..self.selector_vec.len() {
let value = ui.selectable_value(&mut &self.selector_vec[i], &self.selector_vec[self.selected], &self.selector_vec[i]);
if (value.clicked()) {
self.selected = i;
}
}
});
ui.end_row();
});
}
fn save(&mut self, _storage: &mut dyn eframe::Storage) {}
fn on_close_event(&mut self) -> bool {
true
}
fn on_exit(&mut self, _gl: Option<&eframe::glow::Context>) {}
fn auto_save_interval(&self) -> std::time::Duration {
std::time::Duration::from_secs(30)
}
fn max_size_points(&self) -> egui::Vec2 {
egui::Vec2::INFINITY
}
fn clear_color(&self, _visuals: &egui::Visuals) -> egui::Rgba {
// NOTE: a bright gray makes the shadows of the windows look weird.
// We use a bit of transparency so that if the user switches on the
// `transparent()` option they get immediate results.
egui::Color32::from_rgba_unmultiplied(12, 12, 12, 180).into()
// _visuals.window_fill() would also be a natural choice
}
fn persist_native_window(&self) -> bool {
true
}
fn persist_egui_memory(&self) -> bool {
true
}
fn warm_up_enabled(&self) -> bool {
false
}
fn post_rendering(&mut self, _window_size_px: [u32; 2], _frame: &eframe::Frame) {}
}

Cannot move out of an 'Rc'

Deriving PartialEq on an enum with a unit variant of an Rc trait object seems to trigger a Cannot move out of an 'Rc' error.
I've been able to create a small code sample that reproduces the error. Is there a way to fix the error without manually implementing PartialEq for the enum? This happens in 5 places in a codebase.
Code Example:
use failure::Error;
use serde_derive::{Deserialize,Serialize};
use std::rc::Rc;
trait MyTrait {
}
impl PartialEq for MyTrait {
fn eq(&self, rhs: &Self) -> bool {
true
}
}
impl MyTrait for String {
}
#[derive(PartialEq)]
enum MyEnum {
A,
B(Rc<dyn MyTrait>),
}
fn main() -> Result<(), Error> {
println!("{}",MyEnum::A == MyEnum::B(Rc::new(String::from("whee"))));
Ok(())
}
Here's the generated PartialEq:
#[inline]
fn eq(&self, other: &MyEnum) -> bool {
{
let __self_vi =
unsafe { ::std::intrinsics::discriminant_value(&*self) } as
isize;
let __arg_1_vi =
unsafe { ::std::intrinsics::discriminant_value(&*other) } as
isize;
if true && __self_vi == __arg_1_vi {
match (&*self, &*other) {
(&MyEnum::B(ref __self_0), &MyEnum::B(ref __arg_1_0)) =>
(*__self_0) == (*__arg_1_0),
_ => true,
}
} else { false }
}
}
And here's a link to the issue:
https://github.com/rust-lang/rust/issues/31740
So this might just be a language bug right now. Manually implementing PartialEq for all the enums this is an issue for isn't feasible right now, so I'd be curious if there's a workaround.

How do I conditionally check if an enum is one variant or another?

I have an enum with two variants:
enum DatabaseType {
Memory,
RocksDB,
}
What do I need in order to make a conditional if inside a function that checks if an argument is DatabaseType::Memory or DatabaseType::RocksDB?
fn initialize(datastore: DatabaseType) -> Result<V, E> {
if /* Memory */ {
//..........
} else if /* RocksDB */ {
//..........
}
}
First have a look at the free, official Rust book The Rust Programming Language, specifically the chapter on enums.
match
fn initialize(datastore: DatabaseType) {
match datastore {
DatabaseType::Memory => {
// ...
}
DatabaseType::RocksDB => {
// ...
}
}
}
if let
fn initialize(datastore: DatabaseType) {
if let DatabaseType::Memory = datastore {
// ...
} else {
// ...
}
}
==
#[derive(PartialEq)]
enum DatabaseType {
Memory,
RocksDB,
}
fn initialize(datastore: DatabaseType) {
if DatabaseType::Memory == datastore {
// ...
} else {
// ...
}
}
matches!
This is available since Rust 1.42.0
fn initialize(datastore: DatabaseType) {
if matches!(datastore, DatabaseType::Memory) {
// ...
} else {
// ...
}
}
See also:
How to compare enum without pattern matching
Read from an enum without pattern matching
Compare enums only by variant, not value
https://doc.rust-lang.org/std/macro.matches.html
// A simple example that runs in rust 1.58:
enum Aap {
Noot(i32, char),
Mies(String, f64),
}
fn main() {
let aap: Aap = Aap::Noot(42, 'q');
let noot: Aap = Aap::Mies(String::from("noot"), 422.0);
println!("{}", doe(aap));
println!("{}", doe(noot));
}
fn doe(a: Aap) -> i32 {
match a {
Aap::Noot(i, _) => i,
Aap::Mies(_, f) => f as i32,
}
}

Resources