How to specify conditional code in procedural macros - rust

I am building a procedural macro in Rust that implements, for a given struct, the fmt::Display trait for it. Although the implementation does not change the most of the time, there are some cases the subject struct contains some fields that must be displayed in a specific way.
Inside the macro, I did iterate all input fields to determine if the field is present or not. If it does so, I need to add a specific line at the end of the Display implementation. Otherwise, the line must be ignored.
So, I was wondering, is there any way to add conditional code? Just like I show in the example below:
#[proc_macro_derive(DisplayStruct)]
pub fn display_struct(input: TokenStream) -> TokenStream {
let DeriveInput { ident, data, .. } = parse_macro_input!(input);
match data {
Data::Struct(data_struct) => {
let mut has_field = false;
data_struct.fields.iter().for_each(|field| {
if let Some(ident) = field.ident.as_ref() {
has_field |= ident.eq("my_field_name");
}
});
let code = quote! {
impl std::fmt::Display for #ident {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// ...
// this IF from here is not part of the Display implementation, but a macro conditional that tells to the macro if the contained line should be added or not.
if #has_field {
write!(f, "{}", self.my_field_name.to_string())?;
}
Ok(())
}
}
};
return code.into();
}
_ => {
let error: TokenStream = quote!(compile_error!("chonk UwU");).into();
return error;
}
}
}
So the resulting implementation if my_field_name is present should be:
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// ...
write!(f, "{}", self.my_field_name.to_string())?;
Ok(())
}
Otherwise it must look like this:
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// ...
Ok(())
}

Store this line into Option:
let line = has_field.then(|| {
quote! { write!(f, "{}", self.my_field_name.to_string())?; }
});
Then you can interpolate it into quote!:
let code = quote! {
impl std::fmt::Display for #ident {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// ...
#line
Ok(())
}
}
};

Related

Pattern matching with enum in Rust

I'm doing some Rust exercises and I'm struggling a lot. Hope I'm not the only one.
I'm searching for a way to match the pattern for only the coins that have a state.
Coin::*(state) => result.push_str(format!("{:?} from {:?}", coin, state).as_str()),
#[derive(Debug)]
#[allow(dead_code)]
pub enum State {
Alabama,
Alaska
}
#[derive(Debug)]
#[allow(dead_code)]
pub enum Coin {
Penny,
Nickel,
Dime,
Quarter(State)
}
fn main() {
let coin = Coin::Quarter(State::Alabama);
let penny = Coin::Penny;
get_coin_information(&penny);
get_coin_information(&coin);
}
pub fn get_coin_information(coin: &Coin)
{
let mut result = String::from("The coin is a ");
match coin {
Coin::*(state) => result.push_str(format!("{:?} from {:?}", coin, state).as_str()),
other => result.push_str(format!("{:?}", other).as_str())
}
let result = result + ".";
println!("{}", result);
}
There is no such pattern.
See pattern chapter of the book for more information about patterns, and the patterns reference for their grammar. No pattern allows a wildcard in this position.
In my opinion, there are two issues in this task arising from the example, one that Chayim Friedman and mcarton hinted at and the display of type information as string.
What mcarton has provided is very helpful.
Your code with a little modification will also work
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=af81626a5caeaedf49866ed2346e3948
use std::fmt::{self, Debug, Display};
#[derive(Debug)]
pub enum State {
Alabama,
Alaska
}
#[derive(Debug)]
pub enum Coin {
Penny,
Nickel,
Dime,
Quarter(State)
}
impl Display for State {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl Display for Coin {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
fn main() {
let coin = Coin::Quarter(State::Alabama);
let penny = Coin::Penny;
get_coin_information(&penny);
get_coin_information(&coin);
}
pub fn get_coin_information(coin: &Coin)
{
match coin {
//It is written so that it is easy to debug.
Coin::Penny | Coin::Nickel | Coin::Dime => {
let msg = format!("The coin status {}", coin);
println!("{}", msg);
},
_ => {
println!("The coin status other {:?}", coin);
}
}
}

How to downcast Rc<RefCell<dyn io::Write>> into a concrete type?

I want to make a struct which the text output can be either displayed on the console or stored in an internal buffer. If the text is buffered, then I need a method that gives back the text content.
For this aim I use a property named writer, which is dyn std::io::Write (wrapped into Rc<RefCell<>> because it is needed by my real code). Then on struct construction, I create either a io::stdout() instance or a Vec::<u8>::new() instance for this property.
use std::rc::Rc;
use std::cell::RefCell;
use std::io;
struct A {
// Rc<RefCell<>> is needed in my real code
writer: Rc<RefCell<dyn io::Write>>,
}
impl A {
pub fn new() -> Self {
Self { writer: Rc::new(RefCell::new(io::stdout())) }
}
pub fn new_buffered() -> Self {
Self { writer: Rc::new(RefCell::new(Vec::<u8>::new())) }
}
pub fn write(&self, s: &str) {
let mut writer = self.writer.borrow_mut();
writeln!(writer, "{}", s).unwrap();
}
/// Returns None if the struct is not buffered, otherwise a copy of the buffered output.
pub fn get_buffer(&self) -> Option<String> {
match GET_VEC_U8() { // <- Unable to implement this line
Some(vec_u8) => {
Some(String::from_utf8(vec_u8.clone()).unwrap())
},
None => None,
}
}
}
fn main() {
let a = A::new();
a.write("foo");
println!("Buffer: {:?}", a.get_buffer());
let b = A::new_buffered();
b.write("bar");
println!("Buffer: {:?}", b.get_buffer());
}
Question
But I can't figure out how to extract the text content (method get_buffer()), when the writer is Vec<u8>. How can I do it ?
My try
I tried to wrap the property into a Box:
struct A {
writer: Rc<RefCell<Box<dyn io::Write>>>,
}
then use Box::downcast() on it:
impl A {
pub fn get_buffer(&self) -> Option<String> {
let writer = self.writer.borrow();
match (*writer).downcast::<Vec<u8>>() {
Ok(vec_u8) => Some(String::from_utf8(vec_u8.clone()).unwrap()),
Err(_) => None,
}
}
}
but I get this error:
error[E0599]: no method named `downcast` found for struct `std::boxed::Box<dyn std::io::Write>` in the current scope
--> src/main.rs:27:25
|
27 | match (*writer).downcast::<Vec<u8>>() {
| ^^^^^^^^ method not found in `std::boxed::Box<dyn std::io::Write>`
As #SvenMarnach wrote in the comments, writing a custom trait depending on io::Write can be a solution
use std::rc::Rc;
use std::cell::RefCell;
use std::io::{self, Stdout};
trait MyWrite: io::Write {
fn get_buffer(&self) -> Option<String>;
}
impl MyWrite for Stdout {
fn get_buffer(&self) -> Option<String> {
None
}
}
impl MyWrite for Vec<u8> {
fn get_buffer(&self) -> Option<String> {
Some(String::from_utf8(self.clone()).unwrap())
}
}
struct A {
// Rc<RefCell<>> is needed in my real code
writer: Rc<RefCell<dyn MyWrite>>,
}
impl A {
pub fn new() -> Self {
Self { writer: Rc::new(RefCell::new(io::stdout())) }
}
pub fn new_buffered() -> Self {
Self { writer: Rc::new(RefCell::new(Vec::<u8>::new())) }
}
pub fn write(&self, s: &str) {
let mut writer = self.writer.borrow_mut();
writeln!(writer, "{}", s).unwrap();
}
/// Returns None if the struct is not buffered, otherwise a copy of the buffered output.
pub fn get_buffer(&self) -> Option<String> {
let writer = self.writer.borrow();
writer.get_buffer()
}
}
fn main() {
let a = A::new();
a.write("foo");
println!("Buffer: {:?}", a.get_buffer());
let b = A::new_buffered();
b.write("bar");
println!("Buffer: {:?}", b.get_buffer());
}

Can a macro simplify trait impl?

I want to simplify files and indention levels for implementing several Traits for a given struct. Is there a way I can do something like this?
struct Foo { /* ommitted */ }
impl(Default(Foo)) fn default() -> Self {
/* do something */
}
Obviously, it is possible to write
impl Default for Foo {
fn default() -> Self {
/* do something */
}
}
but the enclosing text provides no more value than the macro I hope exists. Although I am new to Rust, I am well aware of (and frequently use)
#[derive(Default)]
but I am looking for a way to simplify actually implementing many traits. I would consider the following clear as well, but AFAIK it is not legal Rust. Note that the following is similar to rust-lang #1250:
impl Foo {
type Index::Output = Bar;
trait Default fn default() -> Self {
// ...
}
trait Debug fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// ...
}
trait Display fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// ...
}
trait Index<usize> fn index(&self, (r, c): (usize, usize)) -> &Self::Index::Output {
// ...
}
fn my_custom_thing() -> () {
// ...
}
}
Whenever I tried something like this, I found that it was easier to just write out the impls.
Thus, I would advise against it. But if you insist, you could start out with the following:
macro_rules! quick_impl {
($structname: ident, Default, $($t:tt)*) => {
impl Default for $structname {
fn default() -> Self {
Self {$($t)*}
}
}
};
($structname: ident, Debug, $($field:ident),*) => {
impl std::fmt::Debug for $structname {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
f.debug_struct(stringify!($tyname))
$(
.field(stringify!($field), &self.$field)
)*
.finish()
}
}
};
// here be more implementations
}
Usable like this:
struct S {
a: usize,
b: bool,
c: char,
}
quick_impl!(S, Default, a:1, b:true, c:'c');
quick_impl!(S, Debug, a, b, c);
This macro goes a step further in that you do not have to say trait or fn in the arguments (as this is clear anyway).

Why is the width ignored for my custom formatter implementation?

When I have an enum with Display implemented and I try to print it formatted, the width I give it is ignored.
use std::fmt;
enum TestEnum {
A,
B,
}
impl fmt::Display for TestEnum {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
TestEnum::A => write!(f, "A"),
TestEnum::B => write!(f, "B"),
}
}
}
fn main() {
println!("-{value:>width$}-", value = TestEnum::A, width = 3);
}
I expect it to output - A-, but it outputs -A-.
If I replace the value with the actual string instead of the enum, it does the right thing,
println!("-{value:>width$}-", value = "A", width = 3);
outputs
- A-
Why is the width being ignored? What do I need to do differently?
By using write! in your fmt implementation, you are overriding the format provided by its caller.
Instead you should call fmt on the strings themselves:
impl fmt::Display for TestEnum {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
TestEnum::A => "A".fmt(f),
TestEnum::B => "B".fmt(f),
}
}
}
(Permalink to the playground)
This will correctly print - A-.

How to implement Display on a trait object where the types already implement Display

I have some code which returns a trait object of type MyTrait so that it can return one of several different structs. I would like to implement the Display trait for the trait object so that I can print the object, with the details delegated to the various structs as they each need their own custom formatters.
I can achieve this by including a formatting method as part of the MyTrait definition, and then implementing Display for MyTrait and delegating - like this:
trait MyTrait {
fn is_even(&self) -> bool;
fn my_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result;
}
impl fmt::Display for MyTrait {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.my_fmt(f)
}
}
However, I already have the Display trait implemented for each of the structs which implement MyTrait. This means I end up with two methods for each struct which do the same thing - the fmt() method to satisfy the Display trait directly on the struct, and the my_fmt() method which is called by the code above. This seems clumsy and repetitive. Is there a simpler way to do it?
Here's a complete example program which illustrates the point. It's a little longer than I would have liked (it's based on the answer to my previous question Calling functions which return different types with shared trait and pass to other functions), but I couldn't think of a simpler way to illustrate the point. Of course, in this toy example the structs and the fmt functions are very simple; in my real application they are more complex.
use std::fmt;
trait MyTrait {
fn is_even(&self) -> bool;
fn my_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result;
}
struct First {
v: u8,
}
struct Second {
v: Vec<u8>,
}
impl MyTrait for First {
fn is_even(&self) -> bool {
self.v % 2 == 0
}
fn my_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.v)
}
}
impl MyTrait for Second {
fn is_even(&self) -> bool {
self.v[0] % 2 == 0
}
fn my_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.v[0])
}
}
fn make1() -> First {
First { v: 5 }
}
fn make2() -> Second {
Second { v: vec![2, 3, 5] }
}
// Implement Display for the structs and for MyTrait
impl fmt::Display for First {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.v)
}
}
impl fmt::Display for Second {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.v[0])
}
}
impl fmt::Display for MyTrait {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.my_fmt(f)
}
}
fn requires_mytrait<T: MyTrait + ?Sized>(v: &&T) {
println!("{:?}", v.is_even());
}
fn main() {
for i in 0..2 {
let v1;
let v2;
let v = match i {
0 => {
v1 = make1();
println!("> {}", v1); // Demonstrate that Display
// is implemented directly
// on the type.
&v1 as &MyTrait
}
_ => {
v2 = make2();
println!("> {}", v2); // Demonstrate that Display
// is implemented directly
// on the type.
&v2 as &MyTrait
}
};
requires_mytrait(&v);
println!("{}", v); // Here I print the trait object
}
}
Can anyone suggest a simpler, cleaner way to do this?
You can make Display a supertrait of MyTrait.
trait MyTrait: fmt::Display {
fn is_even(&self) -> bool;
}
This will make trait objects of MyTrait be Display. This only works if you expect all implementors of MyTrait to implement Display, but that was also the case in your previous solution.

Resources