Rust macro for creating new struct from struct - struct

Is there any way to create a macro in rust that returns a new struct with the fields that I want. I have been working on this for weeks and cannot find a suitable solution. Seeing the problem below, is this even possible? And if so, how would I go about starting to implement this?
struct Person {
id: u32,
first_name: String,
middle_name: Option<String>,
last_name: String,
age: u8,
}
// I want to be able to have a function-like macro
// that takes the new name, old struct, and fields I want as input
// and returns a new struct based on that
sub_struct!("PersonNames", &Person, vec!["first_name", "middle_name", "last_name"])
// the above code yields the following
struct PersonNames {
first_name: String,
middle_name: Option<String>,
last_name: String,
}
another solution I can think of trying that I haven't yet is using derive-like macros.
#[sub_struct(&Person, vec!["first_name", "middle_name", "last_name"])]
struct PersonName {}

Related

Best way to populate a struct from a similar struct?

I am new to Rust, and am attempting to take a struct returned from a library (referred to as source struct) and convert it into protobuf message using prost. The goal is to take the source struct, map the source struct types to the protobuf message types (or rather, the appropriate types for prost-generated struct, referred to as message struct), and populate the message struct fields using fields from the source struct. The source struct fields are a subset of message struct fields. For example:
pub struct Message {
pub id: i32,
pub email: String,
pub name: String,
}
pub struct Source {
pub email: Email,
pub name: Name,
}
So, I would like to take fields from from Source, map the types to corresponding types in Message, and populate the fields of Message using Source (fields have the same name). Currently, I am manually assigning the values by creating a Message struct and doing something like message_struct.email = source_struct.email.to_string();. Except I have multiple Message structs based on protobuf, some having 20+ fields, so I'm really hoping to find a more efficient way of doing this.
If I understand you correctly you want to generate define new struct based on fields from another. In that case you have to use macros.
https://doc.rust-lang.org/book/ch19-06-macros.html
Also this question (and answer) could be useful for you Is it possible to generate a struct with a macro?
To convert struct values from one to another struct best way is to use From<T> or Into<T> trait.
https://doc.rust-lang.org/std/convert/trait.From.html
This is called FRU (functional record update).
This currently works only for structs with the same type and structs with the same type modulo generic parameters.
The RFC-2528 talks about a generalization that would make this work:
struct Foo {
field1: &'static str,
field2: i32,
}
struct Bar {
field1: f64,
field2: i32,
}
let foo = Foo { field1: "hi", field2: 1 };
let bar = Bar { field1: 3.14, ..foo };
Unfortunately, this has not yet been implemented.
You could make a method to create a Message from a Source like this.
impl Message {
pub fn from_source(source: &Source, id: i32) -> Self {
Message {
id: id,
email: source.email.to_string(),
name: source.name.to_string(),
}
}
}
And then,
let source = // todo
let id = // todo
let message = Message::from_source(&source, id);

How to handle struct with procedural macro?

I need dynamic handling of structure fields and their values. So, I need to dynamically read the name of the structure field and the value it contains for further processing. I understood how to get the name of the field, but I don’t quite understand how I can get the value that is stored in a specific field of the structure.
Something similar does the library serde and validator.
For example i have a struct:
#[derive(StackOverflow)]
struct User {
email: String,
username: String,
password: String,
}
And in another crate I create:
#[proc_macro_derive(StackOverflow, attributes(stackoverflow))]
pub fn derive_stackoverflow(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();
...
}

Write a struct with variable content as the field name

I need to write a bunch of struct with similar name within it. Such as:
pub struct ContactUpdate {
pub full_name: String,
pub full_address: String,
/// .... many other fields
}
pub struct Contact {
pub contact_id: Option<ObjectId>,
pub full_name: String,
pub full_address: String,
pub created_at: DateTime
/// .... many other fields
}
/// ... other structs with similar content/field name
I'm lazy. So instead of hard-coding each field name by hand, I think how can I make the field name of the struct into contants with fewer characters so I don't have to type as much. Also with other benefits to export that constants and use it in other files that need it.
pub const ID: &str = "contact_id";
pub const NAME: &str = "full_name";
pub const TIME: &str = "created_at";
pub const ADDR: &str = "full_address";
pub struct Contact {
pub ID: Option<ObjectId>,
pub NAME: String,
pub ADDR: String,
pub TIME: DateTime
/// .... many other fields
}
pub struct ContactUpdate {
pub NAME: String,
pub ADDR: String,
/// .... many other fields
}
Is this possible?
No. It is impossible.
If you really want (don't!) you can have a macro for that.
However, the entire reason for the existence of field names is for the programmers to know what they mean. If you want to use them as constants, you just give them no meaning and can get rid of them completely. At that time, you're back to the old days where variable name lengths were limited to 7-8 characters and people used all sorts of bad and unreadable codes to work with that. Don't do that.

Accessing struct field by a given string

Assume we have a Person struct and a PersonName struct that used by a field, like the following:
struct Person {
name: PersonName,
age: u8,
}
struct PersonName {
name: String,
middle_name: Option<String>,
surname: Option<String>,
}
Create the struct:
let foo = Person { name: PersonName{name: String::from("bar"), middle_name: None, surname: String::from("baz") }, age: 16u8 };
How can I use this string to gain access to the struct field specified in it?
let foo_surname = eval("name.surname") // returns "baz"
// struct evaluator returns the value of specific field
// i.e.: field_path: "my_struct_field.sub_field1.sub_sub_field_1"
fn eval(field_path: String) -> ...{}
Is there any way to do this without writing a custom evaluator?
There is no way to do this.
Rust is a statically compiled language where this kind of information doesn't existing anymore during the program runtime.

Is it possible to get a value in a structure by field name without implementing the `Index` trait?

I have a struct:
struct Person {
first_name: String,
last_name: String
}
I want to get the field by a variable:
let person = Person {
first_name: "Chris".to_string(),
last_name: "Tom".to_string()
};
let field_name = "last_name";
Is it possible to use some code like this without implementing the Index trait?
let field_value = person[field_name]
No.
You will need to write something to do the lookup yourself. Be that implementing Index or a method.

Resources