How do you feature-gate trait derivation? - rust

Lets say you got an enum like this:
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum MyEnum {
One,
Two,
Three,
Four,
}
But you only want to derive, let's say PartialEq, if a certain feature (let's call it myenum-partialeq) is enabled.
How is this done idiomatically in Rust?

Use #[cfg_attr()]:
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "myenum-partialeq", derive(PartialEq)]
pub enum MyEnum {
One,
Two,
Three,
Four,
}
One thing that is nice to remember, is to always put #[derive(Copy)] on the same line or above #[derive(Clone)] if the struct is not generic. This is because if #[derive(Clone)] is on the same #[derive()] or below #[derive(Copy)] it expands to just performing a bitwise copy, while if it is above it performs a full-fledged clone() for each field, and that takes time and may not even be possible to optimize.

Related

How do add generic types in nested structs in Rust?

I know that there is a similar question here, but I've not been able to make it fit my use case.
I have a Model struct that's nested into other structs. The model can have two different types of Config objects, a ModelConfig or a SeedConfig. They are nearly identical save for a few fields. As it stands now, I need two concrete implementations of Model (SeedModel and ModelModel) in order to change the config field, resulting in duplication of all the methods and trait implementations.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct MetaModel {
pub model: Model
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct Model {
pub name: String,
pub config: Option<ModelConfig>
}
What I've tried:
Using Generics: This pushes the generic type up the chain and results in very complex definitions and areas where I don't have the context to create the parent struct (i.e. the MetaModel definition has no access to the Model definition at creation).
This eventually results in a the type parameter C is not constrained by the impl trait, self type, or predicates unconstrained type parameter error
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct MetaModel<C> {
pub model: Model<C>
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct Model<C> {
pub name: String,
pub config: Option<C>
}
Trait Objects: This doesn't work because serde cannot serialize trait objects
pub trait Config {}
pub struct ModelConfig;
impl Config for ModelConfig {}
pub struct SeedConfig;
impl Config for SeedConfig {}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct Model {
pub name: String,
pub config: Option<Box<dyn Config>
}
What I'd like to do:
impl OtherTrait for Model {
type Value = Model;
fn build_model(&self, m: DerivedMeta) -> Result<Self::Value, &'static str> {
Ok(Model {
// Either a SeedConfig or a ModelConfig
})
}
}
What I would do is use a combination of #[serde(flatten)] and #[serde(untagged)]:
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
struct Config {
members: i32,
shared_by: String,
both: i64,
#[serde(flatten)]
specific: SpecificConfig,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(untagged)]
enum SpecificConfig {
SeedConfig {
some_members: i16,
unique_to: String,
seed_config: u64,
},
ModelConfig {
other_members: i8,
not_shared_with_above: u32,
},
}
Serde explanation of flatten:
Flatten the contents of this field into the container it is defined in.
This removes one level of structure between the serialized representation and the Rust data structure representation.
Serde explanation of untagged:
There is no explicit tag identifying which variant the data contains. Serde will try to match the data against each variant in order and the first one that deserializes successfully is the one returned.
By combining these two, we get the following behavior:
flatten allows all shared fields and specific fields to be on the same level in the config
untagged allows us to avoid adding an explicit tag in the config
all shared properties are directly accessible
only specific properties require matching the specific enum

How do I approach this trait object problem?

I have code similar to the one below (playground):
trait SomeTrait {
fn some_fn(&self) -> i32;
}
#[derive(Debug, Clone, Eq, PartialEq)]
enum Foo {
Bar,
Baz(Box<dyn SomeTrait>),
}
fn main() {
// Do something with Foo
}
I have a trait object being held in Foo::Baz and I want whatever to be in there to have Debug, Clone, and Eq derived. The above code won't compile and you'll get an error saying that the trait object doesn't implement the derived traits. I get why it doesn't compile. But I want to know how I can solve this problem. In my actual project, all the derived traits are necessary so simply removing them isn't an option. I thought about using super traits but when you try to write something like:
trait SomeTrait: DynClone + std::fmt::Debug + Eq + PartialEq {
fn some_fn(&self) -> i32;
}
This would require the struct that SomeTrait is being implemented for to have already derived all of the required traits so that it can be used in Foo. Unfortunately now I get an error saying that the trait cannot be made into an object, and I don't understand what it means by that. Can anybody help me figure out how to tackle this problem?
The error means that not every trait can be made into a trait object. Here is more detailed explanation. You can not use Self in generic parameters of such traits while all Clone, Eq and PartialEq do use Self. When using dyn (dynamic dispatch) there isn't enough information to know what Self is and therefore what type of reference the methods of Clone, Eq or PartialEq can accept(explained here and here).
Why don't you go with static dispatch instead of dynamic dispath? Then everything works and you have improved performance at runtime
use std::fmt::Debug;
trait SomeTrait{
fn some_fn(&self) -> i32;
}
#[derive(Debug, Clone, Eq, PartialEq)]
enum Foo<T:SomeTrait> {
Bar,
Baz(T),
}
fn main() {
// Do something with Foo
}

`std::alloc::Allocator` is not satisfied

I have an enum that has two variants, right, and left, these two variants contain a Vec<Token, Expr>. Here are their definitions:
#[derive(Debug, Clone, PartialEq)]
pub enum Token {
...
}
#[derive(Debug, Clone)]
pub enum Expr {
Var(Token, Token),
}
#[derive(Debug, Clone)]
pub enum Side {
Left(Vec<Token, Expr>),
Right(Vec<Token, Expr>),
}
I want each variant of side to contain any combination of Token or Expr. This can be in any order. When I run this, I get an error saying that std::alloc::Allocator is not satisfied for Expr. Why is Allocator not satisfied for Expr but for Token? Also, how do I fix this issue?

Is there any way to simplify deriving for structs with unused generic parameters?

I'm trying to add some type safety to some structs to secure correct usage. For example, an ID is bound to the type of the entity it identifies.
pub trait MarkerEntity {
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct A;
impl MarkerEntity for A {}
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct B;
impl MarkerEntity for B {}
use std::marker::PhantomData;
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct ID<T>
where T: MarkerEntity {
value: u64,
entity_type: PhantomData<T>,
}
impl<T> ID<T>
where T: MarkerEntity {
pub fn new(value: u64) -> Self {
Self { value, entity_type: PhantomData }
}
}
To use such pattern I need to derive everything I would need on my marker structs to pass the bounds checks of the further derives (or use derivative crate).
Is there any way to simplify what I want in the current version of Rust?
I suppose that const generics would make it easier but, AFAIK, they won't land anytime soon.
I don't think there is a much nicer way to do what you want. You could utilize macros to reduce some of boilerplate. If you have a lot of marker struct then at least you only have to define the derives for all markers in one place.
trait MarkerEntity {}
macro_rules! marker_entity {
($Name:ident) => {
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct $Name;
impl MarkerEntity for $Name {}
}
}
marker_entity!(A);
marker_entity!(B);

#[derive(Insertable)] is not implemented for `std::string::String`

I get this error:
#[derive(Insertable, Queryable, Identifiable, Debug, PartialEq)]
^^^^^^^^^^ the trait `diesel::Expression` is not implemented for `std::string::String`
when I try to compile this struct:
#[derive(Insertable, Queryable, Identifiable, Debug, PartialEq)]
#[table_name = "example_table"]
pub struct SigninLog {
pub id: i32,
pub user_group: UserRoleEnum,
pub created_at: Option<SystemTime>,
pub optional_data: Option<String>
}
Is it because it contains a custom enum or an Option<String>? And if that is the problem how can I solve it?
In general, in order to be able to #[derive(X)] for a struct, all its members must implement X as well. This might not be the case with Insertable, because it is not a standard trait, but you might want to verify this; in your case Insertable is not implemented for the String within optional_data; it is implemented for Option<T>, so the Option enclosing it is not an issue.
You might want to implement Insertable manually; I haven't used diesel, though - there might be a simpler way to do this.

Resources