invoke another macro in struct define - rust

I made a simple macro that defines a struct.
macro_rules! __body {
($($body:tt)+) => {
$($body)+
};
}
macro_rules! new_struct {
($(#[$attr:meta])* struct $name:ident { $($body:tt)+ } ) => {
$(#[$attr])* struct $name {
__body!($($body)+);
}
};
}
new_struct! {
#[derive(Deserialize)]
struct Test {
a: bool,
b: String,
}
}
When I compile this code, it raises an error:
|
14 | __body!($($body)+);
| ^ expected `:`
...
19 | / new_struct! {
20 | | #[derive(Deserialize)]
21 | | struct Test {
22 | | a: bool,
23 | | b: String,
24 | | }
25 | | }
| |_- in this macro invocation
|

According to the reference:
Macros can expand to expressions, statements, items (including traits, impls, and foreign items), types, or patterns.
and struct fields are none of these, so you cannot use a declarative macro for that. You may want to try procedural macros instead.

Related

async syntactic oddity

Why does Rust allow this:
fn main() {
let f = |x: bool| {
async {
return
}
};
}
But not this? Specifically, the error complains that the branches return different types, when they appear to be exactly the same.
fn main() {
let f = |x: bool| {
if true {
async {
return
}
} else {
async {
return
}
}
};
}
error[E0308]: `if` and `else` have incompatible types
--> src/main.rs:42:13
|
37 | / if true {
38 | | async {
| _|_____________-
39 | | | return
40 | | | }
| |_|_____________- expected because of this
41 | | } else {
42 | / | async {
43 | | | return
44 | | | }
| |_|_____________^ expected `async` block, found a different `async` block
45 | | }
| |_________- `if` and `else` have incompatible types
|
Every time you write async { }, the compiler makes a unique anonymous Future type. They will be distinct even if two are syntactically equivalent. This is the same for closures.
So your first snippet is simply returning an object (with an anonymous type), while your second is trying to return different types conditionally, which is not allowed. Consider using a trait object so they are the same type:
use std::future::Future;
fn main() {
let f = |x: bool| {
if true {
Box::new(async {
return
}) as Box<dyn Future<Output=()>>
} else {
Box::new(async {
return
})
}
};
}

rust struct fields generating by macro

I want to create a macro which on the received model generates structures with all its fields and implements trait Default, but faced such problem.
My macro struct_field! is recognized as a field, not a macro inside the model!.
Мaybe someone knows how to solve this problem
macro_rules! struct_field {
($fild_name:ident: string = $def_val:expr,) => {
pub $fild_name: Arc<RwLock<String>>
};
($fild_name:ident: string = $def_val:expr, $($fields:tt)+) => {
pub $fild_name: Arc<RwLock<String>>
struct_field!($($fields)+)
};
($fild_name:ident: bool = $def_val:expr,) => {
pub enable_kernel_settings: Arc<AtomicBool>,
};
($fild_name:ident: bool = $def_val:expr, $($fields:tt)+) => {
pub $fild_name: Arc<AtomicBool>,
struct_field!($($fields)+)
};
}
macro_rules! model {
($model:ident { $($inner_model:ident : $inner_model_name:ident { $($fields:tt)+ },)+ }) => {
pub struct $model {$(
$inner_model: $inner_model_name,
)+}
$(pub struct $inner_model_name {
struct_field!($($fields)+) // <-- error
})+
};
}
error:
error: expected `:`, found `!`
--> .../mod.rs:43:29
|
43 | struct_field!($($fields)+)
| ^ expected `:`
...
48 | / model! {
49 | | MainStruct {
50 | | inner_struct1: InnerStruct1 {
51 | | feild1: bool = true,
... |
58 | | }
59 | | }
| |_- in this macro invocation
example:
model! {
MainStruct {
inner_struct1: InnerStruct1 {
feild1: bool = true,
},
inner_struct2: InnerStruct2 {
feild1: bool = false,
feild2: bool = false,
feild3: string = "ignore",
},
}
}
expected result:
pub struct MainStruct {
inner_struct1: InnerStruct1,
inner_struct2: InnerStruct2,
}
pub struct InnerStruct1 {
feild1: Arc<AtomicBool>,
}
pub struct InnerStruct2 {
feild1: Arc<AtomicBool>,
feild2: Arc<AtomicBool>,
feild3: Arc<RwLock<String>>
}
you can test this with "cargo expand"
and you should be enabled rust-nightly
Contrary to C-like macros, Rust macros can only operate on valid AST nodes, that is, they can only be passed something that can be tokenized and, if needed, parsed to some extent, and can only "return" a valid AST node. This, in particular, rules out the invocation of a macro that would be extended as a field definition, because something like a: bool is not a valid AST node.
It is still possible to make a macro to do what you want to do, using function-style procedural macros, but it may become somehow more convoluted.

Obtain a reference from a RefCell<Option<Rc<T>>> in Rust

I am having problem getting a reference out of a RefCell<Option<Rc>>.
Any suggestion?
struct Node<T> {
value: T
}
struct Consumer3<T> {
tail: RefCell<Option<Rc<Node<T>>>>,
}
impl<T> Consumer3<T> {
fn read<'s>(&'s self) -> Ref<Option<T>> {
Ref::map(self.tail.borrow(), |f| {
f.map(|s| {
let v = s.as_ref();
v.value
})
})
}
}
Gives:
error[E0308]: mismatched types
--> src/lib.rs:15:13
|
15 | / f.map(|s| {
16 | | let v = s.as_ref();
17 | | v.value
18 | | })
| |______________^ expected reference, found enum `Option`
|
= note: expected reference `&_`
found enum `Option<T>`
help: consider borrowing here
|
15 | &f.map(|s| {
16 | let v = s.as_ref();
17 | v.value
18 | })
|
error: aborting due to previous error
Playground
Mapping from one Ref to another requires that the target already exist in memory somewhere. So you can't get a Ref<Option<T>> from a RefCell<Option<Rc<Node<T>>>>, because there's no Option<T> anywhere in memory.
However, if the Option is Some, then there will be a T in memory from which you can obtain a Ref<T>; if the Option is None, obviously you can't. So returning Option<Ref<T>> may be a viable alternative for you:
use std::{cell::{Ref, RefCell}, rc::Rc};
struct Node<T> {
value: T
}
struct Consumer3<T> {
tail: RefCell<Option<Rc<Node<T>>>>,
}
impl<T> Consumer3<T> {
fn read(&self) -> Option<Ref<T>> {
let tail = self.tail.borrow();
if tail.is_some() {
Some(Ref::map(tail, |tail| {
let node = tail.as_deref().unwrap();
&node.value
}))
} else {
None
}
}
}
Playground.

How to map or transform a vector of structs into another vector of structs?

Minimal Reproducible Example
pub struct User {
pub id: i32,
pub name: String,
pub match_id: i32,
}
pub struct Match {
pub id: i32,
pub name: String,
}
pub struct MatchWithUsers {
pub id: i32,
pub name: String,
pub users: Vec<User>,
}
fn main() {
let query_result: Vec<(Match, Option<User>)> = vec![
(
Match {
id: 1,
name: String::from("1st match"),
},
Some(User {
id: 1,
name: String::from("Jack"),
match_id: 1,
}),
),
(
Match {
id: 2,
name: String::from("2nd match"),
},
Some(User {
id: 2,
name: String::from("John"),
match_id: 2,
}),
),
(
Match {
id: 3,
name: String::from("3rd match"),
},
None,
),
];
let mut response: Vec<MatchWithUsers> = Vec::new();
for (m, u) in &query_result {
let existing_match = &response
.into_iter()
.find(|match_with_user| match_with_user.id == m.id);
match existing_match {
Some(found_match) => {
println!("Inser user into match: {}", found_match.name);
match u {
Some(mut user) => {
found_match.users.push(user);
}
None => println!("No users."),
}
}
None => {
println!("No existing match. Add to response.");
let user = u.as_ref().unwrap();
response.push(MatchWithUsers {
id: m.id,
name: m.name.clone(),
users: vec![],
});
}
}
}
println!("Response with: {}", response.len());
}
warning: unused variable: `user`
--> src/main.rs:69:21
|
69 | let user = u.as_ref().unwrap();
| ^^^^ help: consider prefixing with an underscore: `_user`
|
= note: `#[warn(unused_variables)]` on by default
warning: variable does not need to be mutable
--> src/main.rs:61:26
|
61 | Some(mut user) => {
| ----^^^^
| |
| help: remove this `mut`
|
= note: `#[warn(unused_mut)]` on by default
error[E0382]: use of moved value: `response`
--> src/main.rs:53:31
|
50 | let mut response: Vec<MatchWithUsers> = Vec::new();
| ------------ move occurs because `response` has type `std::vec::Vec<MatchWithUsers>`, which does not implement the `Copy` trait
...
53 | let existing_match = &response
| ^^^^^^^^ value moved here, in previous iteration of loop
error[E0507]: cannot move out of `u.0` which is behind a shared reference
--> src/main.rs:60:23
|
60 | match u {
| ^
61 | Some(mut user) => {
| --------
| |
| data moved here
| move occurs because `user` has type `User`, which does not implement the `Copy` trait
error[E0596]: cannot borrow `found_match.users` as mutable, as it is behind a `&` reference
--> src/main.rs:62:25
|
62 | found_match.users.push(user);
| ^^^^^^^^^^^^^^^^^ `found_match` is a `&` reference, so the data it refers to cannot be borrowed as mutable
Playground
My problem
I have an API test project using Rocket and Diesel.
The following method does a Diesel query and should map the result to a JSON response. This is a response of all matches in the database with thier users. The users should be nested in each Match node.
My solution attempt
I create a vector.
Iterate in the query result;
Check if the match already exists in my vector, if it does, add the user info (coming from the query result) and add it to the users attribute in the current MatchWithUser, otherwise just add a struct (MatchWithUsers) to the vector.
pub fn show_all_matches2() -> Vec<MatchWithUsers> {
use schema::*;
let connection = establish_connection();
let query_result: Vec<(Match, Option<User>)> = matches::table
.left_join(users::table.on(users::match_id.eq(matches::id)))
.load(&connection)
.expect("Error loading matches");
let mut response: Vec<MatchWithUsers> = Vec::new();
for (m, u) in &query_result {
let existing_match = &response
.into_iter()
.find(|match_with_user| match_with_user.id == m.id);
match existing_match {
Some(mut found_match) => {
println!("Inser user into match: {}", found_match.name);
found_match.users.push(u.unwrap());
}
None => {
println!("No existing match. Add to response.");
let user = u.as_ref().unwrap();
response.push(MatchWithUsers {
id: m.id,
name: m.name.clone(),
players_count: m.players_count,
users: vec![User {
id: user.id,
name: user.name.clone(),
match_id: user.match_id,
}],
});
}
}
}
response
}
Structs
use crate::schema::{matches, users};
use serde::{Deserialize, Serialize};
#[derive(Queryable, Identifiable, Associations, Serialize, Deserialize)]
#[belongs_to(Match)]
#[table_name = "users"]
pub struct User {
pub id: i32,
pub name: String,
pub match_id: i32,
}
#[derive(Queryable, Identifiable, Serialize, Deserialize)]
#[table_name = "matches"]
pub struct Match {
pub id: i32,
pub name: String,
pub players_count: i32,
}
#[derive(Serialize, Deserialize)]
pub struct MatchWithUsers {
pub id: i32,
pub name: String,
pub players_count: i32,
pub users: Vec<User>,
}
Errors
$ cargo run
Compiling got_board_api_v3 v0.1.0 (/Users/tauil/Projects/got/got-board-api-v3)
error[E0382]: use of moved value: `response`
--> src/lib.rs:54:31
|
51 | let mut response: Vec<MatchWithUsers> = Vec::new();
| ------------ move occurs because `response` has type `std::vec::Vec<models::MatchWithUsers>`, which does not implement the `Copy` trait
...
54 | let existing_match = &response
| ^^^^^^^^ value moved here, in previous iteration of loop
error[E0507]: cannot move out of `existing_match.0` which is behind a shared reference
--> src/lib.rs:58:15
|
58 | match existing_match {
| ^^^^^^^^^^^^^^
59 | Some(mut found_match) => {
| ---------------
| |
| data moved here
| move occurs because `found_match` has type `models::MatchWithUsers`, which does not implement the `Copy` trait
error[E0507]: cannot move out of `*u` which is behind a shared reference
--> src/lib.rs:61:40
|
61 | found_match.users.push(u.unwrap());
| ^
| |
| move occurs because `*u` has type `std::option::Option<models::User>`, which does not implement the `Copy` trait
| help: consider borrowing the `Option`'s content: `u.as_ref()`
Query result in the Postgres client:
select * from matches left join users on matches.id = users.match_id;
id | name | players_count | id | name | match_id
----+---------------------+---------------+----+-----------+----------
1 | My first match | 3 | 1 | Rafael | 1
1 | My first match | 3 | 2 | Leandro | 1
1 | My first match | 3 | 3 | Vagner | 1
2 | Just a second match | 4 | 4 | Vagner | 2
2 | Just a second match | 4 | 5 | Leandro | 2
2 | Just a second match | 4 | 6 | Rafael | 2
2 | Just a second match | 4 | 7 | Wanderson | 2
3 | Amazing match | 6 | | |
(8 rows)
You can get a list of matches with the users associated with each match very easily if you use group_by from the itertools crate and if you ensure that your query results are sorted by match ID:
use itertools::Itertools; // 0.9.0
let response: Vec<_> = query_result
.into_iter()
// Note that this assumes that `query_result` is sorted
// by match id since `group_by` only considers
// consecutive matches.
.group_by(|(m, _)| m.id)
.into_iter()
.map(|(id, mut g)| {
// Now `g` is an iterator of `(Match, Option<User>)`
// where all the matches are the same. We take the
// first item to get the match information. Note
// that it is safe to unwrap here because `group_by`
// would never call us with an empty `g`.
let (m, u) = g.next().unwrap();
MatchWithUsers {
id: id,
name: m.name,
// We got the first user along with the match
// information, now we append the other users
// from the remaining items in `g`.
users: u
.into_iter()
.chain(g.flat_map(|(_, u)| u.into_iter()))
.collect(),
}
})
.collect();
Playground

How to return a struct with a generic type that implements the `Read` and `Write` traits?

I am trying to wrap a TcpStream and TlsStream in one object so that I can interface with either of them using one struct. I am trying to delegate the io methods to one or the other based on a config value but can't figure out how to return a struct with a generic type that implements the Read and Write traits
My code is as follows
pub struct TcpStream<T: Read + Write> {
io_delegate: T,
config: Config,
}
impl<T> TcpStream<T>
where T: Read + Write
{
pub fn connect<A: ToSocketAddrs>(config: Config, addr: A) -> io::Result<TcpStream<T>> {
let tcp_stream = net::TcpStream::connect(addr).unwrap();
if config.ssl {
let tls_stream = TlsConnector::builder()
.unwrap()
.build()
.unwrap()
.connect("rem", tcp_stream)
.unwrap();
return Ok(TcpStream {
config: config,
io_delegate: tls_stream,
});
}
return Ok(TcpStream {
config: config,
io_delegate: tcp_stream,
});
}
}
When I try to compile I get the following errors
error[E0308]: mismatched types
--> src/rem/tcp_stream.rs:19:23
|
19 | return Ok(TcpStream {
| _______________________^ starting here...
20 | | config: config,
21 | | io_delegate: tls_stream
22 | | });
| |_____________^ ...ending here: expected type parameter, found struct `native_tls::TlsStream`
|
= note: expected type `rem::tcp_stream::TcpStream<T>`
found type `rem::tcp_stream::TcpStream<native_tls::TlsStream<std::net::TcpStream>>`
error[E0308]: mismatched types
--> src/rem/tcp_stream.rs:24:19
|
24 | return Ok(TcpStream{
| ___________________^ starting here...
25 | | config: config,
26 | | io_delegate: tcp_stream
27 | | });
| |_________^ ...ending here: expected type parameter, found struct `std::net::TcpStream`
|
= note: expected type `rem::tcp_stream::TcpStream<T>`
found type `rem::tcp_stream::TcpStream<std::net::TcpStream>`
Is there a way to achieve this sort of thing?
I'm not sure if this is the best solution but it does seem to work.
I created a new trait which is a combination of Read + Write then just stored it as a Box in my struct
trait ReadWrite : Read + Write {}
impl<T: Read + Write> ReadWrite for T {}
pub struct TcpStream{
io_delegate : Box<ReadWrite>,
config: Config
}
impl TcpStream {
pub fn connect<A: ToSocketAddrs>(config: Config, addr: A) -> TcpStream {
let tcp_stream = net::TcpStream::connect(addr).unwrap();
if config.ssl {
let tls_stream = TlsConnector::builder().unwrap().build().unwrap().connect("rem", tcp_stream).unwrap();
return TcpStream {
config: config,
io_delegate: Box::new(tls_stream)
};
}
return TcpStream{
config: config,
io_delegate:Box::new(tcp_stream)
};
}
}

Resources