I'm relatively new to Rust and I'm exercising with macros.
The goal is to archive some sort if React-ish syntax within Rust lang.
I’m aware of the drastic DSL approach like jsx!(<div />), which is a good solution but of another topic. I’m seeking something in the middle, more like Flutter or SwiftUI syntax, which leverages the native language feature as much as possible, but still presents a visually hierarchical code structure.
Here I'm exploring the idea of constructing a View struct using a children! macro.
My main intension here is to enable View.children field to hold an arbitrary arity tuple. Since variadic generic tuple is not available in Rust yet, macro seems to be the only option.
See the code and comment:
struct View<T> {
// children field holds arbitrary nullable data
// but what i really want is Variadic Tuple, sadly not available in rust
children: Option<T>,
}
// `children!` macro is a shorthand for Some(VariadicTuple)
// so to avoid the ugly `Some((...))` double parens syntax.
macro_rules! children {
($($x:expr),+ $(,)?) => (
Some(($($x),*))
);
}
fn main() {
let _view = View {
children: children!(
42,
"Im A String",
View {
children: Some(("foo", "bar")),
},
),
};
}
So far so good. Now there's this final part I really want to further optimize:
View { children: children!(...) }
Is it possible to call a macro right inside the struct body, (instead of at the value position after a field: syntax) so to eliminate the need to write children: field redundantly?
Yes and no. You can not create something exactly as you describe but you can do something very very similar.
You can implement a new function for the View struct like so:
struct View<T> {
// children field holds arbitrary Option<data>.
children: Option<T>,
}
impl<T> View<T> {
pub fn new() -> Self {
Self {
children: children!(
42,
"Im A String",
View {
children: Some(("foo", "bar")),
},
)
}
}
}
// `children!` macro is a shorthand for Some(VariadicTuple)
// so to avoid the ugly `Some((...))` double parens syntax.
macro_rules! children {
($($x:expr),+ $(,)?) => (
Some(($($x),*))
);
}
And then to create a view object you would do this:
fn main() {
let _view1 = View::new();
let _view2 = View::new();
let _view3 = View::new();
// _view1, _view2, and _view3 are *exactly* the same
}
Or if you want to add arguments, something like this with some modifications for your use case:
struct View<T> {
// children field holds arbitrary Option<data>.
children: Option<T>,
}
impl<T> View<T> {
pub fn new(age: i32, string: &str) -> Self {
Self {
children: children!(
age,
string,
View {
children: Some(("foo", "bar")),
},
)
}
}
}
// `children!` macro is a shorthand for Some(VariadicTuple)
// so to avoid the ugly `Some((...))` double parens syntax.
macro_rules! children {
($($x:expr),+ $(,)?) => (
Some(($($x),*))
);
}
And then implementing it:
fn main() {
let _view1 = View::new(42, "Im A String");
let _view2 = View::new(35, "Im another string");
let _view3 = View::new(12, "This is yet another string");
// _view1, _view2, and _view3 are *not* the same because they have differing "age" and "string" attributes.
}
I am here to help if this is not what you are looking for or if you want more help.
Related
Is it possible to get a value from a struct using a string key?
Like this:
struct Messages {
greetings: String,
goodbyes: String
}
impl Structure {
fn new() -> Structure{
Messages {
greetings: String::from("Hello world"),
goodbyes: String::from("Bye!")
}
}
fn main() {
let messages = Messages::new();
// now how do I print it out with a string key?
println!("{}",messages["greetings"]); // prints "Hello world"
// and can I even turn a struct into an array? Function name is made up
let arr = Struct::to_array(messages);
}
Pls help thz
In short, no, this is not possible. The names of your fields aren't necessarily available at runtime to perform such a check. However, there are other ways to achieve similar results:
use a HashMap<String, String>
write a function to do it:
impl MyStruct {
pub fn get_field(&self, key: String) -> &str {
if key == 'field1' {
self.field1
} else if ...
}
}
Write a derive macro to generate the above function automatically (https://crates.io/crates/field_names might be a useful starting point for how you might go about writing a derive macro)
Or solve it a different way
This pattern is not well supported in Rust, especially compared to more dynamic languages like JavaScript. A problem many new Rust learners face is solving problems "the Rust way".
It's not 100% clear from your comment, but it sounds like your struct is representing a tic-tac-toe board and looks something like this:
struct Board {
top_right: String,
top_middle: String,
top_left: String,
// etc...
}
While this works, there are much better ways of doing this. For example, you could represent each tile with an enum instead of a String, and you could also use an Vec (similar to arrays/lists from other languages) to store that data:
enum Tile {
Empty,
Cross,
Circle,
}
struct Board {
tiles: Vec<Tile>,
}
impl Board {
pub fn print(&self) {
for tile in self.tiles {
println!("{}", match tile {
Tile::Empty => " ",
Tile::Cross => "X"
Tile::Circle => "O",
});
}
}
}
In Chapter 4 of "Programming Rust" by Jim Blandy & Jason Orendorff it says,
It follows that the owners and their owned values form trees: your owner is your parent, and the values you own are your children. And at the ultimate root of each tree is a variable; when that variable goes out of scope, the entire tree goes with it. We can see such an ownership tree in the diagram for composers: it’s not a “tree” in the sense of a search tree data structure, or an HTML document made from DOM elements. Rather, we have a tree built from a mixture of types, with Rust’s single-owner rule forbidding any rejoining of structure that could make the arrangement more complex than a tree. Every value in a Rust program is a member of some tree, rooted in some variable.
An example is provided,
This is simplified and pretty, but is there any mechanism to generate an "ownership tree" visualization with Rust or the Rust tooling? Can I dump an ownership tree when debugging?
There isn't really a specific tool for it, but you can get pretty close by deriving the Debug trait. When you derive the Debug trait for a struct, it will give you a recursive representation of all owned data, terminating at primitive types such as str, u32 etc, or when it encounters a custom Debug implementation.
For example, this program here:
use rand;
#[derive(Debug)]
enum State {
Good,
Bad,
Ugly(&'static str),
}
#[derive(Debug)]
struct ExampleStruct {
x_factor: Option<f32>,
children: Vec<ExampleStruct>,
state: State,
}
impl ExampleStruct {
fn random(max_depth: usize) -> Self {
use rand::Rng;
let mut rng = rand::thread_rng();
let child_count = match max_depth {
0 => 0,
_ => rng.gen::<usize>() % max_depth,
};
let mut children = Vec::with_capacity(child_count);
for _ in 0..child_count {
children.push(ExampleStruct::random(max_depth - 1));
}
let state = if rng.gen() {
State::Good
} else if rng.gen() {
State::Bad
} else {
State::Ugly("really ugly")
};
Self {
x_factor: Some(rng.gen()),
children,
state,
}
}
}
fn main() {
let foo = ExampleStruct::random(3);
dbg!(foo);
}
prints something like this:
[src/main.rs:51] foo = ExampleStruct {
x_factor: Some(
0.27388978,
),
children: [
ExampleStruct {
x_factor: Some(
0.5051847,
),
children: [
ExampleStruct {
x_factor: Some(
0.9675246,
),
children: [],
state: Ugly(
"really ugly",
),
},
],
state: Bad,
},
ExampleStruct {
x_factor: Some(
0.70672345,
),
children: [],
state: Ugly(
"really ugly",
),
},
],
state: Bad,
}
Note that not all the data is in line: The children live somewhere else on the heap. They are not stored inside the ExampleStruct, they are simply owned by it.
This could get confusing if you store references to things, because Debug may start to traverse these references. It does not matter to Debug that they aren't owned. In fact, this is the case with the &'static str inside State::Ugly. The actual bytes that make up the string are not owned by any variable, they are hard coded and live inside the program itself. They will exist for as long as the program is running.
I'm trying to model a structure for a UI library where there exists a ViewNode which owns a RenderNode which owns a LayoutNode. These structures should at the same time form three distinct trees. A ViewTree, a RenderTree, and a Layout tree.
Is there any way of modeling this ownership without resorting to use of Rc? I don't want to use Rc<> because the ownership is clear from my point of view, The trees should never own their children (except for ViewNode), the wrapper is the owner. Each layer should also be able to be pulled out into a library and I don't want to force users of the library to use Rc<>.
Below is what I would want to do but what doesn't work. Should I go about this in a different way perhaps?
#[derive(Debug)]
struct LayoutNode<'a> {
// .. Some fields
children: Vec<&'a LayoutNode<'a>>,
}
#[derive(Debug)]
struct RenderNode<'a> {
// .. Some fields
layout_node: LayoutNode<'a>,
children: Vec<&'a RenderNode<'a>>,
}
#[derive(Debug)]
struct ViewNode<'a> {
// .. Some fields
render_node: RenderNode<'a>,
children: Vec<ViewNode<'a>>,
}
fn make_tree<'a>() -> ViewNode<'a> {
let layout_child = LayoutNode { children: vec![] };
let layout = LayoutNode { children: vec![&layout_child] };
let render_child = RenderNode { layout_node: layout_child, children: vec![] };
let render = RenderNode { layout_node: layout, children: vec![&render_child] };
let view_child = ViewNode { render_node: render_child, children: vec![] };
let view = ViewNode { render_node: render, children: vec![view_child] };
view
}
fn main() {
println!("{:?}", make_tree())
}
You can use a memory arena that uses indices instead of reference counted pointers.
Using indextree as an example:
pub struct NodeId {
index: usize,
}
pub struct Node<T> {
parent: Option<NodeId>,
previous_sibling: Option<NodeId>,
next_sibling: Option<NodeId>,
first_child: Option<NodeId>,
last_child: Option<NodeId>,
removed: bool,
/// The actual data which will be stored within the tree
pub data: T,
}
pub struct Arena<T> {
nodes: Vec<Node<T>>,
}
The NodeId struct is a simple integer index.
The nodes contain references to close by nodes (parent, previous_sibling, etc..) so to make for easy traversal.
A downside of this method is that it's very similar to manual memory management, in that you need to ensure that nodes are added/removed correctly to avoid dangling references. indextree has a lot of error checking when adding/removing nodes in the tree for this reason.
You might also want to have a look at petgraph: While this is a Graph instead of a Tree you can use it as a tree.
Serde supports applying custom attributes that are used with #[derive(Serialize)]:
#[derive(Serialize)]
struct Resource {
// Always serialized.
name: String,
// Never serialized.
#[serde(skip_serializing)]
hash: String,
// Use a method to decide whether the field should be skipped.
#[serde(skip_serializing_if = "Map::is_empty")]
metadata: Map<String, String>,
}
I understand how to implement a procedural macro (Serialize in this example) but what should I do to implement #[serde(skip_serializing)]? I was unable to find this information anywhere. The docs don't even mention this. I have tried to look at the serde-derive source code but it is very complicated for me.
First you must register all of your attributes in the same place you register your procedural macro. Let's say we want to add two attributes (we still don't talk what will they belong to: structs or fields or both of them):
#[proc_macro_derive(FiniteStateMachine, attributes(state_transitions, state_change))]
pub fn fxsm(input: TokenStream) -> TokenStream {
// ...
}
After that you may already compile your user code with the following:
#[derive(Copy, Clone, Debug, FiniteStateMachine)]
#[state_change(GameEvent, change_condition)] // optional
enum GameState {
#[state_transitions(NeedServer, Ready)]
Prepare { players: u8 },
#[state_transitions(Prepare, Ready)]
NeedServer,
#[state_transitions(Prepare)]
Ready,
}
Without that compiler will give a error with message like:
state_change does not belong to any known attribute.
These attributes are optional and all we have done is allow them to be to specified. When you derive your procedural macro you may check for everything you want (including attributes existence) and panic! on some condition with meaningful message which will be told by the compiler.
Now we will talk about handling the attribute! Let's forget about state_transitions attribute because it's handling will not vary too much from handling struct/enum attributes (actually it is only a little bit more code) and talk about state_change. The syn crate gives you all the needed information about definitions (but not implementations unfortunately (I am talking about impl here) but this is enough for handling attributes of course). To be more detailed, we need syn::DeriveInput, syn::Body, syn::Variant, syn::Attribute and finally syn::MetaItem.
To handle the attribute of a field you need to go through all these structures from one to another. When you reach Vec<syn:: Attribute> - this is what you want, a list of all attributes of a field. Here our state_transitions can be found. When you find it, you may want to get its content and this can be done by using matching syn::MetaItem enum. Just read the docs :) Here is a simple example code which panics when we find state_change attribute on some field plus it checks does our target entity derive Copy or Clone or neither of them:
#[proc_macro_derive(FiniteStateMachine, attributes(state_transitions, state_change))]
pub fn fxsm(input: TokenStream) -> TokenStream {
// Construct a string representation of the type definition
let s = input.to_string();
// Parse the string representation
let ast = syn::parse_derive_input(&s).unwrap();
// Build the impl
let gen = impl_fsm(&ast);
// Return the generated impl
gen.parse().unwrap()
}
fn impl_fsm(ast: &syn::DeriveInput) -> Tokens {
const STATE_CHANGE_ATTR_NAME: &'static str = "state_change";
if let syn::Body::Enum(ref variants) = ast.body {
// Looks for state_change attriute (our attribute)
if let Some(ref a) = ast.attrs.iter().find(|a| a.name() == STATE_CHANGE_ATTR_NAME) {
if let syn::MetaItem::List(_, ref nested) = a.value {
panic!("Found our attribute with contents: {:?}", nested);
}
}
// Looks for derive impls (not our attribute)
if let Some(ref a) = ast.attrs.iter().find(|a| a.name() == "derive") {
if let syn::MetaItem::List(_, ref nested) = a.value {
if derives(nested, "Copy") {
return gen_for_copyable(&ast.ident, &variants, &ast.generics);
} else if derives(nested, "Clone") {
return gen_for_clonable(&ast.ident, &variants, &ast.generics);
} else {
panic!("Unable to produce Finite State Machine code on a enum which does not drive Copy nor Clone traits.");
}
} else {
panic!("Unable to produce Finite State Machine code on a enum which does not drive Copy nor Clone traits.");
}
} else {
panic!("How have you been able to call me without derive!?!?");
}
} else {
panic!("Finite State Machine must be derived on a enum.");
}
}
fn derives(nested: &[syn::NestedMetaItem], trait_name: &str) -> bool {
nested.iter().find(|n| {
if let syn::NestedMetaItem::MetaItem(ref mt) = **n {
if let syn::MetaItem::Word(ref id) = *mt {
return id == trait_name;
}
return false
}
false
}).is_some()
}
You may be interested in reading serde_codegen_internals, serde_derive, serenity's #[command] attr, another small project of mine - unique-type-id, fxsm-derive. The last link is actually my own project to explain to myself how to use procedural macros in Rust.
After some Rust 1.15 and updating the syn crate, it is no longer possible to check derives of a enums/structs, however, everything else works okay.
You implement attributes on fields as part of the derive macro for the struct (you can only implement derive macros for structs and enums).
Serde does this by checking every field for an attribute within the structures provided by syn and changing the code generation accordingly.
You can find the relevant code here: https://github.com/serde-rs/serde/blob/master/serde_derive/src/internals/attr.rs
To expand Victor Polevoy's answer when it comes to the state_transitions attribute. I'm providing an example of how to extract the field attribute #[state_transitions(NeedServer, Ready)] on a enum that derives #[derive(FiniteStateMachine)]:
#[derive(FiniteStateMachine)]
enum GameState {
#[state_transitions(NeedServer, Ready)] // <-- extract this
Prepare { players: u8 },
#[state_transitions(Prepare, Ready)]
NeedServer,
#[state_transitions(Prepare)]
Ready,
}
use proc_macro::TokenStream;
#[proc_macro_derive(FiniteStateMachine, attributes(state_transitions))]
pub fn finite_state_machine(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();
// Extract the enum variants
let variants: Vec<&syn::Variant> = match &ast.data {
syn::Data::Enum(ref data_enum) => data_enum.variants.iter().collect(),
other => panic!("#[derive(FiniteStateMachine)] expects enum, got {:#?}", other)
};
// For each variant, extract the attributes
let _ = variants.iter().map(|variant| {
let attrs = variant.attrs.iter()
// checks attribute named "state_transitions(...)"
.find_map(|attr| match attr.path.is_ident("state_transitions") {
true => Some(&attr.tokens),
false => None,
})
.expect("#[derive(FiniteStateMachine)] expects attribute macros #[state_transitions(...)] on each variant, found none");
// outputs: attr: "(NeedServer, Ready)"
eprintln!("attr: {:#?}", attrs.to_string());
// do something with the extracted attributes
...
})
.collect();
...
}
The content of the extracted attrs (typed TokenStream) looks like this:
TokenStream [
Group {
delimiter: Parenthesis,
stream: TokenStream [
Ident {
ident: "NeedServer",
span: #0 bytes(5511..5521),
},
Punct {
ch: ',',
spacing: Alone,
span: #0 bytes(5521..5522),
},
Ident {
ident: "Ready",
span: #0 bytes(5523..5528),
},
],
span: #0 bytes(5510..5529),
},
]
Most of this is boilerplate, provided as a compilable example. Scroll down.
use std::rc::{Rc, Weak};
use std::cell::RefCell;
use std::any::{Any, AnyRefExt};
struct Shared {
example: int,
}
struct Widget {
parent: Option<Weak<Box<Widget>>>,
specific: RefCell<Box<Any>>,
shared: RefCell<Shared>,
}
impl Widget {
fn new(specific: Box<Any>,
parent: Option<Rc<Box<Widget>>>) -> Rc<Box<Widget>> {
let parent_option = match parent {
Some(parent) => Some(parent.downgrade()),
None => None,
};
let shared = Shared{pos: 10};
Rc::new(box Widget{
parent: parent_option,
specific: RefCell::new(specific),
shared: RefCell::new(shared)})
}
}
struct Woo {
foo: int,
}
impl Woo {
fn new() -> Box<Any> {
box Woo{foo: 10} as Box<Any>
}
}
fn main() {
let widget = Widget::new(Woo::new(), None);
{
// This is a lot of work...
let cell_borrow = widget.specific.borrow();
let woo = cell_borrow.downcast_ref::<Woo>().unwrap();
println!("{}", woo.foo);
}
// Can the above be made into a function?
// let woo = widget.get_specific::<Woo>();
}
I'm learning Rust and trying to figure out some workable way of doing a widget hierarchy. The above basically does what I need, but it is a bit cumbersome. Especially vexing is the fact that I have to use two statements to convert the inner widget (specific member of Widget). I tried several ways of writing a function that does it all, but the amount of reference and lifetime wizardry is just beyond me.
Can it be done? Can the commented out method at the bottom of my example code be made into reality?
Comments regarding better ways of doing this entire thing are appreciated, but put it in the comments section (or create a new question and link it)
I'll just present a working simplified and more idiomatic version of your code and then explain all the changed I made there:
use std::rc::{Rc, Weak};
use std::any::{Any, AnyRefExt};
struct Shared {
example: int,
}
struct Widget {
parent: Option<Weak<Widget>>,
specific: Box<Any>,
shared: Shared,
}
impl Widget {
fn new(specific: Box<Any>, parent: Option<Rc<Widget>>) -> Widget {
let parent_option = match parent {
Some(parent) => Some(parent.downgrade()),
None => None,
};
let shared = Shared { example: 10 };
Widget{
parent: parent_option,
specific: specific,
shared: shared
}
}
fn get_specific<T: 'static>(&self) -> Option<&T> {
self.specific.downcast_ref::<T>()
}
}
struct Woo {
foo: int,
}
impl Woo {
fn new() -> Woo {
Woo { foo: 10 }
}
}
fn main() {
let widget = Widget::new(box Woo::new() as Box<Any>, None);
let specific = widget.get_specific::<Woo>().unwrap();
println!("{}", specific.foo);
}
First of all, there are needless RefCells inside your structure. RefCells are needed very rarely - only when you need to mutate internal state of an object using only & reference (instead of &mut). This is useful tool for implementing abstractions, but it is almost never needed in application code. Because it is not clear from your code that you really need it, I assume that it was used mistakingly and can be removed.
Next, Rc<Box<Something>> when Something is a struct (like in your case where Something = Widget) is redundant. Putting an owned box into a reference-counted box is just an unnecessary double indirection and allocation. Plain Rc<Widget> is the correct way to express this thing. When dynamically sized types land, it will be also true for trait objects.
Finally, you should try to always return unboxed values. Returning Rc<Box<Widget>> (or even Rc<Widget>) is unnecessary limiting for the callers of your code. You can go from Widget to Rc<Widget> easily, but not the other way around. Rust optimizes by-value returns automatically; if your callers need Rc<Widget> they can box the return value themselves:
let rc_w = box(RC) Widget::new(...);
Same thing is also true for Box<Any> returned by Woo::new().
You can see that in the absence of RefCells your get_specific() method can be implemented very easily. However, you really can't do it with RefCell because RefCell uses RAII for dynamic borrow checks, so you can't return a reference to its internals from a method. You'll have to return core::cell::Refs, and your callers will need to downcast_ref() themselves. This is just another reason to use RefCells sparingly.