I have this enum:
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)]
pub enum TheAge {
Adult,
Age(u8)
}
And the cli struct
#[derive(Parser)]
#[command(author, version, about, long_about)]
pub struct Cli {
#[arg(short, long, value_enum)]
pub age: TheAge
}
This fails with the error:
error: `#[derive(ValueEnum)]` only supports unit variants. Non-unit variants must be skipped
When I remove the Age(u8) from the enum, this compiles.
Any tips on how to use an enum that is not unit variants?
Related
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
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?
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);
We're trying to return structure in RPC, but as far as I understand it should be serializable:
error[E0277]: the trait bound `pallet_spaces::Space<T>: serde::de::Deserialize<'_>` is not satisfied
--> pallets/spaces/rpc/src/lib.rs:15:1
|
15 | #[rpc]
| ^^^^^^ the trait `serde::de::Deserialize<'_>` is not implemented for `pallet_spaces::Space<T>`
|
= note: required because of the requirements on the impl of `for<'de> serde::de::Deserialize<'de>` for `std::vec::Vec<pallet_spaces::Space<T>>`
= note: required because of the requirements on the impl of `serde::de::DeserializeOwned` for `std::vec::Vec<pallet_spaces::Space<T>>`
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
The problem is that we use T::Moment from pallet_timestamp and it's not serializable, so we stuck at this point:
error[E0277]: the trait bound `<T as pallet_timestamp::Trait>::Moment: _::_serde::Serialize` is not satisfied
--> pallets/spaces/src/lib.rs:25:5
|
25 | pub created: WhoAndWhen<T>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `_::_serde::Serialize` is not implemented for `<T as pallet_timestamp::Trait>::Moment`
|
= note: required because of the requirements on the impl of `_::_serde::Serialize` for `pallet_utils::WhoAndWhen<T>`
= note: required by `_::_serde::ser::SerializeStruct::serialize_field`
What can you suggest to easily return a structure like this?
#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug, Serialize, Deserialize)]
pub struct Space<T: Trait> {
pub id: SpaceId,
pub created: WhoAndWhen<T>,
pub updated: Option<WhoAndWhen<T>>,
pub owner: T::AccountId,
// Can be updated by the owner:
pub parent_id: Option<SpaceId>,
pub handle: Option<Vec<u8>>,
pub content: Content,
pub hidden: bool,
pub posts_count: u32,
pub hidden_posts_count: u32,
pub followers_count: u32,
pub score: i32,
/// Allows to override the default permissions for this space.
pub permissions: Option<SpacePermissions>,
}
#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug, Serialize, Deserialize)]
pub struct WhoAndWhen<T: Trait> {
pub account: T::AccountId,
pub block: T::BlockNumber,
pub time: T::Moment,
}
Your main problem is that you are mixing std and no-std here. Substrate only depends on serde in std mode, as you can learn about in literally any Cargo.toml file in the project.
Start by fixing this: You only derive serde::* when you are in std mode.
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug)]
pub struct Space<T: Trait> {
// snip..
}
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.