The generated enum type looks like this, although I don't really have access to the src as it's generated by Prost! during the build:
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
#[repr(i32)]
pub enum Fruit {
Unspecified = 0,
Apple = 1,
Banana = 2,
Lemon = 3,
...
I can get a &str from an enum like this:
let apple = Fruit::Apple;
assert_eq!(apple.as_str_name(), "APPLE");
This method is provided by Prost!.
But I want to get the enum from a string.
The strum crate offers a solution except I can't edit the source code file. Maybe there's a hack using include!() to attach a macro annotation during the build stage.
prost_build can be configured to attach arbitrary attributes to the generated code via type_attribute and field_attribute:
// build.rs
Config::new()
.type_attribute("Fruit", "#[derive(EnumString)]")
.compile_protos(["src/fruit.proto"], &["src"])
.unwrap();
Related
const CELL_ALIGNMENT: usize = std::mem::align_of::<EntryType>();
#[repr(align(CELL_ALIGNMENT))]
pub struct AlignedCell;
#[repr(C)]
pub struct AlignedHeader {
_align: [AlignedCell; 0],
count: usize,
}
CELL_ALIGNMENT is a constant. But it looks like repr doesn't allow constant. Only literal is allowed. Is there any way to get around this?
No, you cannot use an expression in #[repr(align(_))]. It must be a literal.
The next best thing: you can assert the alignment of your struct matches another at compile-time by using the static-assertions crate:
#[repr(align(8))]
pub struct AlignedCell;
static_assertions::assert_eq_align!(AlignedCell, EntryType);
It doesn't set the alignment automatically, but you'll get a compiler error if its wrong.
I want to define an enum using a macro. The enum needs to implement the strum traits {Display, EnumIter, EnumString}. I also want to keep the warnings missing_copy_implementations, missing_debug_implementations on. I came up with the following snippet:
#![warn(
missing_copy_implementations,
missing_debug_implementations,
)]
macro_rules! define_fruits {
{$($fruit:ident -> $name:literal),* $(,)?} => {
#[derive(Display, EnumIter, EnumString, Clone, Copy, Debug)]
pub enum Fruits {
$(
#[strum(to_string = $name)]
$fruit,
)*
}
};
}
define_fruits! {
Apple -> "green",
Orange -> "orange",
}
The above works fine except I get the warnings:
|
4 | missing_copy_implementations,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: this warning originates in the macro `define_fruits` (in Nightly builds, run with -Z macro-backtrace for more info)
and similar for missing_debug_implementations.
These warnings go away when I remove Display, EnumIter, EnumString from my enum. They also go away if I define the same enum outside of a macro.
I cannot seem to find a way to get rid of the warnings in the above scenario, can someone help?
TL/DR: the problem is with derive(EnumIter), which isn't compatible with these lints. I've opened the issue on strum repository asking for possible changes.
To see what really happens, let's try to cargo expand the current code. After some simplification and stubbing out the unnecessary parts, we can see the following structure:
///An iterator over the variants of [Self]
pub struct FruitsIter {
idx: usize,
back_idx: usize,
marker: PhantomData<()>,
}
This is a type you get when you call IntoEnumIterator::iter derived for your enum. There's no Copy or Debug implementation for this struct - they are neither derived nor explicitly added by strum, so the lint sees the violation and fires.
A hint on the problem source can be seen when looking at the exact error, as provided by cargo check (or at the eror highlighting from rust-analyzer):
warning: type could implement `Copy`; consider adding `impl Copy`
--> src/lib.rs:10:27
|
10 | #[derive(Display, EnumIter, EnumString, Clone, Copy, Debug)]
| ___________________________^
11 | | pub enum Fruits {
| |___________^
Note that the span of the "erroneous" code starts from the EnumIter - since that's the token which the FruitsIter's span is tied to, warning on the FruitsIter is shown as a warning on the derive(EnumIter). And indeed, if we drop this derive - warning disappears.
There's nothing you can do with this, I'm afraid, aside from explicitly allowing these lints for the whole module containing your enum. This is something that should probably be fixed by the strum maintainers.
I need my structs to be hashable. The structs never mutate after creation so it could be possible to pre-calculate and store it as a field.
An example struct looks like this:
#[derive(Clone, Debug, Deserialize)]
pub struct Predicate {
note: Option<String>,
arguments: Option<HashMap<String, Value>>,
value: Value,
}
I tried to add Hash directive to the struct , but Value doesn't implement Hash
tried to implement myself
impl Hash for Value {
but getting error
| ^^^^^^^^^^^^^^-----
| | |
| | `Value` is not defined in the current crate
| impl doesn't use only types from inside the current crate
so 2 possible solutions would suffice for me
calculate and store the hash on deserialization and save as a field of the struct (which never changes)
Implement Hash on Value - but not sure it is possible to do outside of of the serde crate.
Option 1 could work for me (calculate + store during deserialize)
I saw in the documentation https://serde.rs/field-attrs.html#deserialize_with
#[serde(deserialize_with = "path")]
but didn't see any example how it can be done
Maybe there are other approaches?
Since HashMap and Value both don't implement Hash your best bet is to manually implement Hash for Predicate manually. To reduce repetition, I also recommend making a wrapper around Value and implementing hash for that. When you do this, make sure that if a == b then a_hashed == b_hashed. Sample code:
impl Hash for Predicate{
fn hash<H: Hasher>(&self, hasher:&mut H){
self.note.hash(hasher);
self.arguments.iter().for_each(|(key,value)|{
//Without realizing it, I accidentally violated the contract I mentioned above
//Because there is no guarantee about the order of items returned by HashMap.iter().
//Thanks, #Jmb for pointing it out. Let this serve as a warning, be really
//Careful when manually implementing Hash and similar traits.
key.hash(hasher);
MyValue(value).hash(hasher);
});
MyValue(self.value).hash(hasher);
}
}
struct MyValue(Value);
impl Hash for MyValue{
fn hash<H: Hasher>(&self, hasher:&mut H){
//large match statement here
}
}
The OP mentioned that they could guarantee that the objects would be created once and not mutated. If that's the case, you could instead add a unique identifier, such as from the UUID Crate or a timestamp of creation and hash that instead of the much more expensive full hash.
I have an enum in Rust which has one value that takes a String:
#[derive(Clone, Copy)]
enum Simple {
Error(String),
Okay,
Foo([u32; 5]),
}
fn main() {
let x = Simple::Error(String::from("blah"));
let y = x.clone();
}
The enum value Foo above represents about 10 other enums I use that take copyable types or arrays of them. The compiler doesn't seem to complain about them, only the Error(String) which causes this:
error[E0204]: the trait `Copy` may not be implemented for this type
--> src/main.rs:1:17
|
1 | #[derive(Clone, Copy)]
| ^^^^
2 | enum Simple {
3 | Error(String),
| ------ this field does not implement `Copy`
|
For some reason, String is not copyable. I don't get this. How do I implement Clone for an enum for just the one type which has a problem while using the default impl for the rest?
Copy
Copy designates types for which making a bitwise copy creates a valid instance without invalidating the original instance.
This isn't true for String, because String contains a pointer to the string data on the heap and assumes it has unique ownership of that data. When you drop a String, it deallocates the data on the heap. If you had made a bitwise copy of a String, then both instances would try to deallocate the same memory block, which is undefined behaviour.
Since String doesn't implement Copy, your enum cannot implement Copy either because the compiler enforces that Copy types are composed only of Copy data members.
Clone
Clone merely provides a standard clone method, and it's up to each implementor to decide how to implement it. String does implement Clone, so you can put #[derive(Clone)] on your enum.
I did some exploring to see what a manual implementation would look like for an enum. I came up with this, but keep in mind you can also do #[derive(Clone)] as stated elsewhere and the compiler will do this for you.
enum Simple {
Error(String),
Okay,
Foo([u32; 5]),
}
impl Clone for Simple {
fn clone(&self) -> Simple {
match self {
Error(a) => Error(a.to_string()),
Okay => Okay,
Foo(a) => Foo(a.clone()),
}
}
}
I have an enum in Rust which has one value that takes a String:
#[derive(Clone, Copy)]
enum Simple {
Error(String),
Okay,
Foo([u32; 5]),
}
fn main() {
let x = Simple::Error(String::from("blah"));
let y = x.clone();
}
The enum value Foo above represents about 10 other enums I use that take copyable types or arrays of them. The compiler doesn't seem to complain about them, only the Error(String) which causes this:
error[E0204]: the trait `Copy` may not be implemented for this type
--> src/main.rs:1:17
|
1 | #[derive(Clone, Copy)]
| ^^^^
2 | enum Simple {
3 | Error(String),
| ------ this field does not implement `Copy`
|
For some reason, String is not copyable. I don't get this. How do I implement Clone for an enum for just the one type which has a problem while using the default impl for the rest?
Copy
Copy designates types for which making a bitwise copy creates a valid instance without invalidating the original instance.
This isn't true for String, because String contains a pointer to the string data on the heap and assumes it has unique ownership of that data. When you drop a String, it deallocates the data on the heap. If you had made a bitwise copy of a String, then both instances would try to deallocate the same memory block, which is undefined behaviour.
Since String doesn't implement Copy, your enum cannot implement Copy either because the compiler enforces that Copy types are composed only of Copy data members.
Clone
Clone merely provides a standard clone method, and it's up to each implementor to decide how to implement it. String does implement Clone, so you can put #[derive(Clone)] on your enum.
I did some exploring to see what a manual implementation would look like for an enum. I came up with this, but keep in mind you can also do #[derive(Clone)] as stated elsewhere and the compiler will do this for you.
enum Simple {
Error(String),
Okay,
Foo([u32; 5]),
}
impl Clone for Simple {
fn clone(&self) -> Simple {
match self {
Error(a) => Error(a.to_string()),
Okay => Okay,
Foo(a) => Foo(a.clone()),
}
}
}