Closest implementation for enums with fields in pyO3 - rust

As of PyO3 version 0.18.1, only fieldless enums are supported.
What are the potential implementations to support enums with fields using PyO3 if implemented manually?? pyclass is of no use here (Correct me if I am wrong).
For instance if I have to implement for following enum?
pub enum Prop {
Str(String),
I32(i32),
I64(i64),
U32(u32),
U64(u64),
F32(f32),
F64(f64),
Bool(bool),
}
Please suggest.

Related

How do I avoid Enum + Trait pattern when a struct is not object safe?

I get the implications of object safety, but I'm trying to find an idiomatic way to solve for this situation.
Say I have two structs that share common behavior and also need to derive PartialEq for comparison in another part of the program:
trait Growl:PartialEq {
fn growl(&self);
}
#[derive(PartialEq)]
struct Pikachu;
#[derive(PartialEq)]
struct Porygon;
impl Growl for Pikachu {
fn growl(&self) {
println!("pika");
}
}
impl Growl for Porygon {
fn growl(&self) {
println!("umm.. rawr?");
}
}
In another struct, I want to hold a Vec of these objects. Since I can't use a trait object with Vec<Box<Growl>>...
struct Region{
pokemon: Vec<Box<dyn Growl>>,
}
// ERROR: `Growl` cannot be made into an object
... I need to get more creative. I read this article, which suggests using an enum or changing the trait. I haven't yet explored type erasure, but it seems heavy-handed for my use case. Using an enum like this is what I've ended up doing but it feels unnecessarily complex
enum Pokemon {
Pika(Pikachu),
Pory(Porygon),
}
Someone coming through this code in the future now needs to understand the individual structs, the trait (which provides all functionality for the structs), and the wrapper enum type to make changes.
Is there a better solution for this pattern?
I read this article, which suggests using an enum or changing the trait. I haven't yet explored type erasure, but it seems heavy-handed for my use case.
Type erasure is just a synonym term for dynamic dispatch - even your original Box<dyn Growl> "erases the type" of the Pokemon. What you want here is to continue in the same vein, by creating a new trait better taylored to your use case and providing a blanket implementation of that trait for any type that implements the original trait.
It sounds complex, but it's actually very simple, much simpler than erased-serde, which has to deal with serde's behemoth traits. Let's go through it step by step. First, you create a trait that won't cause issues with dynamic dispatch:
/// Like Growl, but without PartialEq
trait Gnarl {
// here you'd have only the methods which are actually needed by Region::pokemon.
// Let's assume it needs growl().
fn growl(&self);
}
Then, provide a blanket implementation of your new Gnarl trait for all types that implement the original Growl:
impl<T> Gnarl for T
where
T: Growl,
{
fn growl(&self) {
// Here in the implementation `self` is known to implement `Growl`,
// so you can make use of the full `Growl` functionality, *including*
// things not exposed to `Gnarl` like PartialEq
<T as Growl>::growl(self);
}
}
Finally, use the new trait to create type-erased pokemon:
struct Region {
pokemon: Vec<Box<dyn Gnarl>>,
}
fn main() {
let _region = Region {
pokemon: vec![Box::new(Pikachu), Box::new(Porygon)],
};
}
Playground

Create struct that is usable with sqlx, has a datetime AND is serializable. Or how to enable serde?

I have a struct that contains a date and I use it with sqlx to retrieve data from my database. So something like:
use sqlx::types::chrono::{DateTime, Utc};
pub struct Account {
pub id: i32,
pub primary_email_id: i32,
pub created: DateTime<Utc>,
}
and
sqlx::query_as!(Account, "select * ...")
This works fine so far. But I also want Account to be serializable via serde. The obvious approach is:
#[derive(Serialize)]
pub struct Account {
...
This fails, because the Serialize trait is not implemented for DateTime<Utc>. I tried the same with PrimitiveDateTime from the time crate with the same result. In theory both should support serde as a feature.
I tried to explicitly add time or chrono as dependency, to enable serde as feature and use the type without the sqlx::types prefix. But in that case it fails because some sqlx traits are not implemented.
I assume that I somehow have to enable the serde feature for the classes brought in by sqlx, but I have no idea how to specify a feature for a feature!?
How to I tell sqlx to enable serde for the time/chrono types?
This depends on the time and chrono Cargo features of sqlx. So ensure the [dependencies] section of your Cargo.toml file includes these. As an example:
sqlx = { version = "0.6.0", features = [ "postgres", "runtime-tokio-native-tls", "offline", "time", "chrono" ] }
(This is essentially the comment of #Stargateur above. All credit to them.)

How do I use serde to serialize a HashMap with non-serializable keys?

I have a HashMap as a value in a struct that I'm manually serializing:
pub struct GitInfo {
pub branches: HashMap<Oid, Branch>,
}
Branch is something I've defined, but Oid is an external type I don't own, with a to_string() method I'd be happy to use...
I've read How do I use Serde to serialize a HashMap with structs as keys to JSON? but it refers to keys the author defined - I can't implement Serialize for Oid as it's not in my crate. And I can't implement Serialize for HashMap<Oid, Branch> for similar reasons.
Is there some way around this? I could build a wrapper struct around HashMap<Oid, Branch> but that seems like overkill.
It was suggested that I look at How to transform fields during serialization using Serde? or How can I implement Serialize using an existing Display trait implementation? - both seem to boil down to using serialize_with - which I could possibly do, but I'd have to use the derive(Serialize) macro, when I was planning to serialize GitInfo manually (see below). Or I could use a wrapper object.
If those are the only options, I could live with it, but it seems a bit surprising that there isn't a simpler way - is there some way to call a function like the serialize_with macro uses, but from within my GitInfo serialization?
Something like:
impl Serialize for GitInfo {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("GitInfo", 2)?;
state.serialize_field("other bits", &self.other_bits)?;
state.serialize_field("branches", /* something */)?;
state.end()
}
}

Is it idiomatic to implement `Deref` as returning a field of a composite structure?

I have:
struct Id;
struct Url;
struct IdAndUrl {
id: Id,
url: Url,
}
I'd like to be able to use IdAndUrl in places where I need just Id. To eliminate the noise I could use impl Deref<Id> for IdAndUrl.
Is this a good practice / idiomatic?
There are indeed close-to-official guidelines related with Deref and DerefMut. According to C-DEREF from the Rust API guidelines, "Only smart pointers implement Deref and DerefMut." Your suggestion to use Deref would lead to multiple issues, and so it is strongly unadvised.
Deref does not have a type parameter, but an associated type. Implementing it would have to be done as the code below, but could never be implemented for additional attributes.
// don't try this at home!
impl Deref for IdAndUrl {
type Target = Id;
fn deref(&self) -> &Self::Target { &self.id }
}
Moreover, a Deref implementation exposes the methods from the target type via deref coercion, polluting the struct with an interface that you might not want to have here.
One could look at the other conversion traits (namely From, AsRef, and Borrow) and see if they make sense (C-CONV-TRAITS). But from my interpretation, none of these would make sense. As already suggested in another answer, a simple getter is ideal here:
impl IdAndUrl {
fn id(&self) -> &Id { &self.id }
}
The intention of Deref is to implement pointer types. It is supposed to convert a pointer to T to T, so dereferencing the pointer feels "natural". Using it to convert between unrelated types seems confusing.
I suggest you add a getter method id() instead.

How can I add the to_string() functionality to an enum?

I am trying to create Error enums that implement to_string(). I have tried to derive(Debug) for them but it doesn't seem to be enough.
Here is the enum that I am working on:
#[derive(Debug, Clone)]
pub enum InnerError {
InnerErrorWithDescription(String),
}
#[derive(Debug, Clone)]
pub enum OuterError {
OuterErrorWithDescription(String),
}
What I am trying to make is:
// result type <T,InnerErrorWithDescription>
result.map_err(|err| { Error::OuterErrorWithDescription(err.to_string())}) // .to_string() is not available
I could not manage to convert InnerError enum type to OuterError.
What should I change to implement it?
I have made an example for writing enum types and their values' here:
Rust Playground
But, still I had to specify the type and it's description in match case, are there any more generic implementation?
Your enum should implement Display; from ToString docs:
This trait is automatically implemented for any type which implements
the Display trait. As such, ToString shouldn't be implemented
directly: Display should be implemented instead, and you get the
ToString implementation for free.
Edit: I have adjusted your playground example; I think you might be after something like this.

Resources