Is it possible to change/generate code through derive macro inside of the struct that uses that macro? - rust

I was curious whether it is possible to change the code inside the struct that uses derive macro, or are you only limited to generating the new code outside?
Example
Adding another field to the Building struct through SomeMacro.
#[derive(SomeMacro)]
pub struct Building {
colour: String,
// Add height: u8 through derive macro
}

It is not possible. This is a fundamental characteristic of derive macros: they take an existing item's tokens and generate new, separate items (usually trait impls).
If you want to modify the struct item itself, you must instead make an attribute macro, which can return a replacement for the input tokens. Attribute macros aren't invoked using the derive attribute but are attributes themselves: #[some_macro] pub struct Building { ...

Related

Dealing with `Options` and defaults when parsing in TOML structs with Rust+Serde

I have been working on configuration parsing code and wondered if you could help me to pick the best types for this code.
I am trying to parse the following TOML:
[default]
a=10
b="abc"
[section1]
a = 78
[section2]
b="xyz"
The types of keys are the same in each section and each field follows the chain of defaults: sectionX.value => default.value => default value hardcoded in Rust via x.value.or(default.value).or(Some(...) for each field.
The most straightforward way to declare it in Rust (including serde attributes)
struct Section{
a: Option<usize>,
b: Option<String>,
}
The problem is that I want to parse all defaults first, and then use a fully materialized struct with no unassigned values in my code.
struct Section{
a: usize,
b: String,
}
I can use the following approaches:
Use original Section struct and always to unwrap/expect because I "know" the defaults have been assigned in the config parsing code. I make a mistake, I can catch panic and that does not look tidy. I'd like to leverage more help from the compiler.
Use the second Section struct (the one that has no Options). I can assign defaults via Serde annotations, but then I am loosing the signal that something was unspecified and needs a default from another level.
Declare both variants of the struct. This is the most verbose variant and I will look even more verbose when I grow 20+ fields or embedded structs in the config.
Generated Solution 3 via macros or some clever typing. Is there a crate for this? Maybe Section could have some generic type that can be Option in one place, but then "newtype" wrapper with a single value somewhere else?
Something else?
Some of the solutions above would work alright, but maybe there is a known elegant solution.

How can I copy the fields of another structure?

Is there any crate or functionality that allows me to copy the fields from another structure?
Crate A
struct Product {
name: String,
sku: String,
amount: i32,
}
Crate B
#[derive(copy_fields_from = "a::Product", InputObject)]
struct ProductApi {}
I have to expose several objects from another crate in an API that uses
"#[derive(InputObject)]". For that, I have to duplicate all the structures of the other structure. Is it possible to copy the fields of another structure? Perhaps using hypothetical syntax such as #[derive(copy_fields_from ="a::Product")].
No, there is no way of doing this cleanly.
If you felt you had to do this, you'd need to write a build script that:
Located the source code of the target crate.
Parsed the source code using a crate like syn.
Located the types in question.
Output them again.
See also:
How do I apply a macro attribute to a function defined in a separate module?
Add Serialize attribute to type from third-party lib
Is it possible for one struct to extend an existing struct, keeping all the fields?
For your specific case, I'd advocate implementing whatever trait(s) by hand.

Is it possible to iterate over an external struct's members in a macro? [duplicate]

I'm interested in using wasm-bindgen via rust-webpack-template to compile Rust code to WebAssembly. However, I'd like to avoid directly wrapping my code with the #[wasm_bindgen] attribute macro directly so that I can separate out the function logic from the generated WebAssembly interface to better organize my project. Instead, I would prefer to have binding generation be in a separate file, for example:
mod my_code;
use my_code::my_function;
#[wasm_bindgen]
my_function; // I want to do something like this!
I understand that #[wasm_bindgen] is a macro attribute that operates on the AST of the function definition that usually follows, but is there an approach for applying that macro to code defined elsewhere?
As far as I know, there's no way to do this. Macros operate on the AST of the code they are attached to, and there's no code to be attached to here.
If you really need this, you'll have to copy-and-paste the signature of your function:
mod my_code {
pub fn my_function(_: i32) -> String {
unimplemented!()
}
}
#[wasm_bindgen]
fn my_function(a: i32) -> String {
my_code::my_function(a)
}
It's possible you could write a macro to make the wrapping slightly less tedious, but you'll still need to replicate the function name, argument types, and return type.

How do I apply a macro attribute to a function defined in a separate module?

I'm interested in using wasm-bindgen via rust-webpack-template to compile Rust code to WebAssembly. However, I'd like to avoid directly wrapping my code with the #[wasm_bindgen] attribute macro directly so that I can separate out the function logic from the generated WebAssembly interface to better organize my project. Instead, I would prefer to have binding generation be in a separate file, for example:
mod my_code;
use my_code::my_function;
#[wasm_bindgen]
my_function; // I want to do something like this!
I understand that #[wasm_bindgen] is a macro attribute that operates on the AST of the function definition that usually follows, but is there an approach for applying that macro to code defined elsewhere?
As far as I know, there's no way to do this. Macros operate on the AST of the code they are attached to, and there's no code to be attached to here.
If you really need this, you'll have to copy-and-paste the signature of your function:
mod my_code {
pub fn my_function(_: i32) -> String {
unimplemented!()
}
}
#[wasm_bindgen]
fn my_function(a: i32) -> String {
my_code::my_function(a)
}
It's possible you could write a macro to make the wrapping slightly less tedious, but you'll still need to replicate the function name, argument types, and return type.

Sharing weak trait object references

I'm trying to provide "views" of non-owned structs to separate components of a system.
Assume a set of traits with distinct methods: Drawable, Modifiable and a number of structs which implement at least one of the traits - SimpleBox, Panel, Expression.
Different components of the system will need to frequently access sequences of these objects, using methods of specific traits; consider a DrawingManager or a ModifyManager:
struct DrawingManager {
items: Vec<Weak<Drawable>>,
}
struct ModifyManager {
items: Vec<Weak<Modifiable>>
}
While a single object may be referenced in both managers, assume that there is a separate single owner of all structs:
struct ObjectManager {
boxes: Vec<Rc<Box>>,
panels: Vec<Rc<Panel>>,
expressions: Vec<Rc<Expression>>,
}
Ideally, it would be useful to be able to manage deleting structs from one place - i.e simply removing it from the ObjectManager being enough to invalidate references in all other components (hence the use of Weak).
Is there a way of doing this?
Is this the correct way to achieve this?
Is there a more idiomatic way of implementing this functionality?
The system contains several traits, so making a single trait using methods of all the other traits seems like a bad idea. Several traits have more than one method, so replacing them with closures is not possible.
What I have tried
As one object may produce one or more Rc<Trait>, we might envision implementing this with a HashMap<ID, Vec<Rc<Any>>> whereby we make each struct have a unique ID, which maps to a list of all Rc that have been made for it.
When we want to remove an object, we remove it from the corresponding list, and remove the entry in the hashmap, invalidating all Weak references.
However, implementing this fails, as to insert into the HashMap, one must upcast a Rc<Trait> -> Rc<Any>, only to downcast it later.
I'm not sure if this is the idiomatic way of doing this, but I've since developed a crate providing this functionality - dependent_view.
Using the crate, the initial problem can be solved by using DependentRc instead of plain Rc's:
struct ObjectManager {
boxes: Vec<DependentRc<Box>>,
panels: Vec<DependentRc<Panel>>,
expressions: Vec<DependentRc<Expression>>
}
let object_manager : ObjectManager = ObjectManager::new();
Then using macros provided by the crate, we can obtain Weak<> references to these structs:
let box_view : Weak<Drawable> = to_view!(object_manager.boxes[0]);
let panel_view : Weak<Drawable> = to_view!(object_manager.panels[0]);
let expression_view : Weak<Drawable> = to_view!(object_manager.expressions[0]);
With this, dropping the corresponding DependentRc<> will invalidate all Weak<> references that have been made of it.

Resources