I'm trying to write a macro that generalizes serde_yaml deserialization for any struct so I don't have to rewrite the same thing over and over again. The only thing that is messign me up right now is the repetition inside a pattern.
Macro:
macro_rules! impl_struct_deserialization {
(
$struct_type: path {
$(
$field_name:ident : $field_type:path
),*
}
) => {
paste! {
impl<'de> Deserialize<'de> for $struct_type {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'de> {
#[derive(Deserialize)]
#[serde(field_identifier, rename_all = "lowercase")]
enum Field {
$(
[<$field_name:camel>]
),*
}
struct [<$struct_type Visitor>];
impl<'de> Visitor<'de> for [<$struct_type Visitor>] {
type Value = $struct_type;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str(&("struct "
.to_owned()
.push_str(&stringify!($struct_type))
)
);
}
fn visit_map<V>(self, mut map: V) -> Result<$struct_type, V::Error>
where V: MapAccess<'de> {
$(let mut $field_name: Option<$field_type> = None;)*
while let Some(key) = map.next_key()? {
match key {
$(Field::[<$field_type:camel>] => {
if $field_name.is_some() {
return Err(serde::de::Error::duplicate_field(stringify!($field_name)));
}
$field_name = Some(map.next_value()?);
})*
}
}
$(
let $field_name = $field_name.ok_or_else(|| serde::de::Error::missing_field(stringify!($field_name)))?;
)*
Ok($struct_type::new($($field_name)*))
}
}
}
}
}
};
}
One of the calls:
impl_struct_deserialization!(
GBox {
center: Vec3f,
material: Material,
radius: f32
}
);
Error (repeats for every field apart from 1st):
Thank you!
UPD: used this as a reference
That specific error is due to a missing comma in a line near the bottom:
Ok($struct_type::new($($field_name),*))
// ^
Related
I am seeking to extend the pattern by utilizing default arguments in Rust as seen in previous question by someone which is using an "arguments" struct and the From/Into traits.
However, as you can see, the code clearly looks very redundant and adding associated functions to Foo struct would require more code for the From/Into traits accordingly.
type FileName = Option<String>;
pub struct BarArg {
a: f64,
b: FileName,
}
impl Default for BarArg {
fn default() -> Self {
BarArg { a: 1.0, b: None }
}
}
impl From<()> for BarArg {
fn from(_: ()) -> Self {
Self::default()
}
}
impl From<f64> for BarArg {
fn from(a: f64) -> Self {
Self {
a,
..Self::default()
}
}
}
impl From<Option<String>> for BarArg {
fn from(b: Option<String>) -> Self {
Self {
b,
..Self::default()
}
}
}
impl From<(f64, Option<String>)> for BarArg {
fn from((a, b): (f64, Option<String>)) -> Self {
Self { a, b }
}
}
pub struct BazArg {
a: i32,
b: FileName,
}
impl Default for BazArg {
fn default() -> Self {
BazArg { a: 1, b: None }
}
}
impl From<()> for BazArg {
fn from(_: ()) -> Self {
Self::default()
}
}
impl From<i32> for BazArg {
fn from(a: i32) -> Self {
Self {
a,
..Self::default()
}
}
}
impl From<Option<String>> for BazArg {
fn from(b: Option<String>) -> Self {
Self {
b,
..Self::default()
}
}
}
impl From<(i32, Option<String>)> for BazArg {
fn from((a, b): (i32, Option<String>)) -> Self {
Self { a, b }
}
}
struct Foo;
impl Foo {
pub fn bar<A>(bar_arg: A)
where
A: Into<BarArg>,
{
match bar_arg.into().b {
Some(b) => {
println!("FileName is: {:?}", b);
}
None => {
println!("No FileName was passed");
}
};
}
pub fn baz<A>(baz_arg: A)
where
A: Into<BazArg>,
{
match baz_arg.into().b {
Some(b) => {
println!("FileName is: {:?}", b);
}
None => {
println!("No FileName was passed");
}
};
}
}
fn main() {
Foo::bar(());
Foo::bar(Some("bar.toml".to_string()));
Foo::baz(());
Foo::baz(Some("baz.toml".to_string()));
}
Playground
Is there any way to somehow implement these codes in a non-redundant and concise manner?
Don't. Do not emulate features, especially when they have good (even better) alternatives. And do not abuse other features to emulate those features.
That being said, it is possible to make it less boilerplate-y using macros. An important point to note is: what if two parameters have the same type? this will generate conflicting From implementations. You have to make sure it either doesn't happen or you only allow omitting the last parameters. You can expose the parameters struct to allow more flexible filling.
The tricky part is that both for allowing all consecutive parameter sequences and for allowing only the last we will need TT munching. This is because macro_rules! cannot extract from the middle of a list, or even from its end. It can only match its start.
Here's a macro that only allows omitting the last arguments. It also always requires you to pass a tuple, so you pass (1,) instead of just 1, for example. It is pretty easy to allow this, but I like the always-tuple approach because I feel it is more consistent (lifting the only-last limit is much harder, but possible):
macro_rules! impl_from_for_default {
// Recursion stop condition
{
$struct_name:ident {}
} => {
impl ::core::convert::From<()> for $struct_name {
fn from((): ()) -> Self {
Self::default()
}
}
};
{
$struct_name:ident {
$($param:ident : $param_ty:ty,)*
}
} => {
impl ::core::convert::From<( $($param_ty,)* )> for $struct_name {
fn from(( $($param,)* ): ( $($param_ty,)* )) -> Self {
Self {
$($param,)*
..Self::default()
}
}
}
omit_last_param! {
$struct_name
[ ]
$($param : $param_ty,)*
}
};
}
macro_rules! omit_last_param {
// Recursion stop condition
{
$struct_name:ident
[ $($first_params:tt)* ]
$last_param:ident : $last_param_ty:ty,
} => {
impl_from_for_default! {
$struct_name {
$($first_params)*
}
}
};
{
$struct_name:ident
[ $($first_params:tt)* ]
$param:ident : $param_ty:ty,
$($rest:tt)*
} => {
omit_last_param! {
$struct_name
[
$($first_params)*
$param : $param_ty,
]
$($rest)*
}
};
}
macro_rules! with_default {
{
$vis:vis $struct_name:ident {
$($param:ident : $param_ty:ty = $param_default:expr),* $(,)?
}
} => {
$vis struct $struct_name {
$($param : $param_ty,)*
}
impl ::core::default::Default for $struct_name {
fn default() -> Self {
Self {
$($param : $param_default,)*
}
}
}
impl_from_for_default! {
$struct_name {
$($param : $param_ty,)*
}
}
};
}
Playground.
Example usage:
with_default! {
pub BazArg {
a: i32 = 1,
b: FileName = None,
}
}
I am new to Rust, and does not fully understand lifetime, so probably, that is why I can't solv the following issue. I need a solution in which a class has a heterogeneous HashMap containing different objects derived from the same trait.
I have to be able to extend an object with some (multiple) functionality dinamically. Other solutions are also welcome. Adding functionality to the class in compile time could also work, but adding functionality directly to the main class not.
use std::collections::HashMap;
trait DoerTrait {
fn do_something( & self, a : u8, b : u8 ) -> u8;
}
struct MyDoer<'a> {
}
impl DoerTrait for MyDoer<'a> {
fn do_something( & self, a : u8, b : u8 ) -> u8 {
return a + b;
}
}
struct MyMain<'a> {
doers : HashMap<u8,&'a dyn DoerTrait>,
}
impl<'a> MyMain<'a> {
fn new() -> Self {
Self {
doers : HashMap::new()
}
}
fn add_doer( &mut self, id : u8, doer : & dyn DoerTrait ) {
self.doers.insert( id, doer );
}
fn do_something( & self, id : u8 ) {
match self.doers.get( &id ) {
Some( doer ) => {
println!( "{}", doer(19,26) );
}
None => {
println!( "Doer not specified!" );
}
}
}
}
fn main() {
let mut mymain = MyMain::new();
let mydoer = MyDoer{};
mymain.add_doer( 42, &mydoer );
mymain.do_something( 42 );
}
Not too sure what issue you have, once MyDoer has been stripped of its incorrect (unnecessary) lifetime and the lifetime has correctly been declared on impl MyMain, the compiler directly points to the parameter of add_doer not matching (after which it points out that doer in do_something is not a function):
use std::collections::HashMap;
trait DoerTrait {
fn do_something(&self, a: u8, b: u8) -> u8;
}
struct MyDoer;
impl DoerTrait for MyDoer {
fn do_something(&self, a: u8, b: u8) -> u8 {
return a + b;
}
}
struct MyMain<'a> {
doers: HashMap<u8, &'a dyn DoerTrait>,
}
impl<'a> MyMain<'a> {
fn new() -> Self {
Self {
doers: HashMap::new(),
}
}
fn add_doer(&mut self, id: u8, doer: &'a dyn DoerTrait) {
self.doers.insert(id, doer);
}
fn do_something(&self, id: u8) {
match self.doers.get(&id) {
Some(doer) => {
println!("{}", doer.do_something(19, 26));
}
None => {
println!("Doer not specified!");
}
}
}
}
fn main() {
let mut mymain = MyMain::new();
let mydoer = MyDoer {};
mymain.add_doer(42, &mydoer);
mymain.do_something(42);
}
I am trying to make a macro that generates a struct with a new function implementation. The new function needs to call functions based on the type of the field and use the return value as the field value.
The new implementation should end up working like this:
struct foo {
test: i32,
other: String,
}
impl foo {
fn new() -> Self {
foo {
test: get_i32(),
other: get_string(),
}
}
}
This is the current code I have:
macro_rules! test {
(struct $name:ident { $($fname:ident : $ftype:ty),* }) => {
#[derive(Debug)]
pub struct $name {
$(pub $fname : $ftype),*
}
impl $name {
fn new(mut v: Vec<u8>) -> Self {
$name {
$($fname : ),*
}
}
}
};
}
I have tried putting a match statement but it gives incompatible arms types error.
impl $name {
fn new(mut v: Vec<u8>) -> Self {
$name {
$($fname : match &stringify!($ftype)[..] {
"i32" => get_i32(),
"String" => get_string(),
}),*
}
}
}
Thank you.
I managed to use another function that returns any to get it to work.
macro_rules! test {
(struct $name:ident { $($fname:ident : $ftype:ty),* }) => {
#[derive(Debug)]
pub struct $name {
$(pub $fname : $ftype),*
}
impl $name {
fn new(mut v: Vec<u8>) -> Self {
$name {
$($fname : get_feild::<$ftype>(stringify!($ftype)).downcast_ref::<$ftype>().unwrap().clone()),*
}
}
}
};
}
fn get_feild<T>(t: &str) -> Box<dyn std::any::Any> {
match t {
"i32" => Box::new(get_i32()),
"String" => Box::new(get_string()),
_ => panic!("UNKNOWN TYPE"),
}
}
I have a bunch of FFI functions that I call using C. The caller expects 1 for success, or -1 on failure.
struct Error;
fn my_rust_function() -> Result<(), Error> {
Ok(())
}
#[allow(non_snake_case)]
pub extern "C" fn Called_From_C() -> i32 {
let result = my_rust_function();
match result {
Ok(_) => 1,
Err(_) => -1,
}
}
Is there a more idiomatic way of converting my Result<(), Error> into the 1 / -1 return code?
I'd create an extension trait:
trait FfiError {
fn as_c_error(&self) -> i32;
}
impl<T, E> FfiError for Result<T, E> {
fn as_c_error(&self) -> i32 {
match self {
Ok(_) => 1,
Err(_) => -1,
}
}
}
Once it's brought into scope, you can call it like any other method:
pub extern "C" fn called_from_c() -> i32 {
my_rust_function().as_c_error()
}
See also:
Is there a way other than traits to add methods to a type I don't own?
You could use repr(transparent) to create a type where you could implement From and that still represent a i32, this allow to compile check that you transform your result correctly assuming you didn't have bug in your from() implementation so maybe add some unit tests.
type MyResult = Result<(), ()>;
fn my_rust_function() -> MyResult {
Ok(())
}
#[repr(transparent)]
pub struct CResult {
code: i32,
// code: libc::c_int, // if your C lib expect a `c_int` and not a `i32`
}
impl From<MyResult> for CResult {
fn from(result: MyResult) -> Self {
let code = match result {
Ok(_) => 1,
Err(_) => -1,
};
Self { code }
}
}
#[allow(non_snake_case)]
pub extern "C" fn Called_From_C() -> CResult {
let result = my_rust_function();
result.into()
}
You could also use enum with repr(i32):
#[repr(i32)]
pub enum CResult {
NoError = 1,
Error = -1,
}
impl From<MyResult> for CResult {
fn from(result: MyResult) -> Self {
match result {
Ok(_) => CResult::NoError,
Err(_) => CResult::Error,
}
}
}
In nightly, you could also implement Try:
#![feature(try_trait)]
use std::ops::Try;
type MyResult = Result<(), ()>;
fn my_rust_function() -> MyResult {
Ok(())
}
#[repr(i32)]
pub enum CResult {
NoError = 1,
Error = -1,
}
impl From<MyResult> for CResult {
fn from(result: MyResult) -> Self {
match result {
Ok(_) => CResult::NoError,
Err(_) => CResult::Error,
}
}
}
impl From<CResult> for MyResult {
fn from(cresult: CResult) -> Self {
match cresult {
CResult::NoError => Ok(()),
CResult::Error => Err(()),
}
}
}
impl Try for CResult {
type Ok = ();
type Error = ();
fn into_result(self) -> MyResult {
self.into()
}
fn from_ok(_: <Self as Try>::Ok) -> Self {
Self::NoError
}
fn from_error(_: <Self as Try>::Error) -> Self {
Self::Error
}
}
#[allow(non_snake_case)]
pub extern "C" fn Called_From_C() -> CResult {
let _ = my_rust_function()?;
CResult::NoError
}
Note: Be careful with the enumeration one, make sure your implementation is compatible. #[repr(libc::c_int)] is what we really want but I don't know any way to express this in Rust. So maybe a structure with repr(transparent) is more safe if the lib expect a c_int.
At the moment, implementing the std::ops::IndexMut trait on a type in Rust requires that I also implement the std::ops::Index trait as well. The bodies of these implementations end up being virtually identical. For example:
use std::ops::{Index, IndexMut};
enum IndexType {
A,
B,
}
struct Indexable {
a: u8,
b: u8,
}
impl Index<IndexType> for Indexable {
type Output = u8;
fn index<'a>(&'a self, idx: IndexType) -> &'a u8 {
match idx {
IndexType::A => &self.a,
IndexType::B => &self.b,
}
}
}
impl IndexMut<IndexType> for Indexable {
fn index_mut<'a>(&'a mut self, idx: IndexType) -> &'a mut u8 {
match idx {
IndexType::A => &mut self.a,
IndexType::B => &mut self.b,
}
}
}
fn main() {}
This works, and obviously for trivial types this isn't a serious problem, but for more complex types with more interesting indexing this quickly becomes laborious and error-prone. I'm scratching my head trying to find a way to unify this code, but nothing is jumping out at me, and yet I feel there has to/should be a way to do this without essentially having to copy and paste. Any suggestions? What am I missing?
Unfortunately, this cuts across a few things Rust really isn't good at right now. The cleanest solution I could come up with was this:
macro_rules! as_expr {
($e:expr) => { $e };
}
macro_rules! borrow_imm { ($e:expr) => { &$e } }
macro_rules! borrow_mut { ($e:expr) => { &mut $e } }
macro_rules! impl_index {
(
<$idx_ty:ty> for $ty:ty,
($idx:ident) -> $out_ty:ty,
$($body:tt)*
) => {
impl ::std::ops::Index<$idx_ty> for $ty {
type Output = $out_ty;
fn index(&self, $idx: $idx_ty) -> &$out_ty {
macro_rules! index_expr { $($body)* }
index_expr!(self, borrow_imm)
}
}
impl ::std::ops::IndexMut<$idx_ty> for $ty {
fn index_mut(&mut self, $idx: $idx_ty) -> &mut $out_ty {
macro_rules! index_expr { $($body)* }
index_expr!(self, borrow_mut)
}
}
};
}
enum IndexType { A, B }
struct Indexable { a: u8, b: u8 }
impl_index! {
<IndexType> for Indexable,
(idx) -> u8,
($this:expr, $borrow:ident) => {
match idx {
IndexType::A => $borrow!($this.a),
IndexType::B => $borrow!($this.b),
}
}
}
fn main() {
let mut x = Indexable { a: 1, b: 2 };
x[IndexType::A] = 3;
println!("x {{ a: {}, b: {} }}", x[IndexType::A], x[IndexType::B]);
}
The short version is: we turn the body of index/index_mut into a macro so that we can substitute the name of a different macro that, given an expression, expands to either &expr or &mut expr. We also have to re-capture the self parameter (using a different name) because self is really weird in Rust, and I gave up trying to make it work nicely.