proc macros for generating mut and non-mut methods without duplicating code

Related tags

Miscellaneous mwt
Overview

mwt

Hey! You! Read this before using!

mwt was thrown together pretty quickly for personal use, because I couldn't find an existing crate that does this.

There are probably bugs, there are definitely plenty of edge cases that haven't been considered, and the error messages are rather poor.

It'll probably get better as I use it and fix issues I find, but caveat emptor or whatever


Generate mut and non-mut versions of the same function without duplicating code!

mwt provides two mostly identical macros: mwt and maybe_mut

  • mwt looks for mwt in identifiers, and looks for types like &Mwt<T>
  • maybe_mut does the same for maybe_mut and &MaybeMut<T>

both let you put #[if_mut] and #[not_mut] before blocks to have conditionally present sections.

they also have a mwt() and maybe_mut() function* respectively for things like return &mwt(self.0)

both also let you pass an argument ignore_self e.g. #[mwt::maybe_mut(ignore_self)] to stop mwt from messing with the &self (or &mut self) parameter. stripping mut from &mut self is the default because taking &T<self> is a parse error, and most of the time this is the desired behavior (at least for my use cases).

there isn't currently a way to handle functions of the form _ref/_mut but one may be added in the future (maybe rwf which becomes either ref or mut?)

*Not actually a function, but the proc macro looks for it being used like a function call

Example:

mwt lets you write:

use mwt::mwt;

struct SomeStruct {
    a_vector: Vec<SomeStruct>,
}

impl SomeStruct {
    #[mwt]
    fn my_mwt_accessor(&mut self) -> &Mwt(SomeStruct) {
        let mut a = 0;
        a = a + 1;
        let b = &mwt(a);
        #[if_mut] {
            println!("Hello from my_mut_accessor()!");
        }
        #[not_mut] {
            println!("Hello from my_accessor()!");
        }
        self.a_vector.get_mwt(0).unwrap()
    }
}

which results in two functions:

impl SomeStruct {
    fn my_accessor(&self) -> &SomeStruct {
        let mut a = 0;
        a = a + 1;
        let b = &a;
        println!("Hello from my_accessor()!");
        self.a_vector.get(0).unwrap()
    }
    fn my_mut_accessor(&mut self) -> &mut SomeStruct {
        let mut a = 0;
        a = a + 1;
        let b = &mut a;
        println!("Hello from my_mut_accessor()!");
        self.a_vector.get_mut(0).unwrap()
    }
}

How to use

e.g.

#[mwt::mwt]
fn my_mwt_method(&'a mut self, other_param: i32) -> &Mwt<bool> {
    #[if_mut] {
        //code for only the mut version of the function
        let i = 0;
    }
    #[not_mut] {
        // code for only the non-mut version of the function
        let i= 1;
    }
    // do something with i
    self.get_mwt_flag_by_index(i)
}

Basically write the mutable version of your function, but for identifiers, replace mut with mwt and for types replace &mut T with &Mwt<T>

Alternatively you can use mwt::maybe_mut if you feel that's more readable. example:

#[mwt::maybe_mut]
pub fn get_maybe_mut<T: 'static + Component>(&mut self) -> Option<&MaybeMut<T>> {
    // use #[if_mut]{} and #[not_mut]{} for conditional behavior
    //      or for cases where mwt isn't powerful enough yet
    //      like .as_ref() vs .as_mut()
    #[if_mut] { println!("if_mut"); }
    #[not_mut] { println!("not_mut"); }
    //   use &MaybeMut<T> for &mut types
    let map: &MaybeMut<HashMap<TypeId,Box<dyn Component>>>
    //    and &maybe_mut(...) for taking mut references
            = &maybe_mut(self.components);
    // use _maybe_mut_ in function calls, etc.
    map.get_maybe_mut(&TypeId::of::<T>())
        .and_then(|c| c.cast_maybe_mut::<T>())
}

results in two functions:

pub fn get_<T: 'static + Component>(& self) -> Option<&T> {
    println!("not_mut"); 
    let map: &HashMap<TypeId,Box<dyn Component>> = &self.components;
    map.get(&TypeId::of::<T>()).and_then(|c| c.cast::<T>())
}
pub fn get_mut<T: 'static + Component>(&mut self) -> Option<&mut T> {
    println!("if_mut");
    let map: &mut HashMap<TypeId,Box<dyn Component>> = &mut self.components;
    map.get_mut(&TypeId::of::<T>()).and_then(|c| c.cast_mut::<T>())
}

What's it actually doing?

mwt::mwt basically just replaces the function with two copies (i.e. a non-mut and mut version) and does a few things on those:

  • replace any occurrences of type references like &Mwt<T> with &T and &mut T respectively
  • replace any occurences of mwt(expr) with expr and mut expr respectively
  • for the non-mut version of the function, it takes all identifiers it finds and trims any starting "mwt_" and ending "_mwt" and replaces "_mwt_" with "_"
  • for the mut version of the function, it takes all identifiers it finds and replaces any instances of "mwt" with "mut"
  • to allow for other ways behavior can differ, the mut version strips any occurences of #[not_mut]{...} and the non-mut version strips any occurrences of #[if_mut]{...} (the ones that aren't stripped have their braces removed, so be aware of that)

mwt::maybe_mut is identical just with different strings.


Found a bug? Need a feature?

Please file an issue or submit a pull request!

You might also like...
A copypastable guide to implementing simple derive macros in Rust.

A copypastable guide to implementing simple derive macros in Rust. The goal Let's say we have a trait with a getter trait MyTrait {

todo-or-die provides procedural macros that act as checked reminders.

todo-or-die provides procedural macros that act as checked reminders.

Frame is a markdown language for creating state machines (automata) in 7 programming languages as well as generating UML documentation.

Frame Language Transpiler v0.5.1 Hi! So very glad you are interested in Frame. Frame system design markdown language for software architects and engin

 Generating the nth Fibonacci number
Generating the nth Fibonacci number

Generating the nth Fibonacci number Per Wikipedia, "In mathematics, the Fibonacci numbers, commonly denoted Fn, form a sequence, called the Fibonacci

Procedural macros for Floccus

floccus-proc Procedural macros for floccus This crate contains procedural attribute macros (currently only one) used by the floccus. But potentially c

A metamacro toolkit for writing complex macros.

Big Mac This crate contains the branching_parser! metamacro, which can be used to create complex macros with few lines of code. To use the macro, call

Web service generating images of Japanese (Riichi) Mahjong hands.
Web service generating images of Japanese (Riichi) Mahjong hands.

chombo-gen ChomboGen is a web service that allows to generate images of Japanese (Riichi) Mahjong hands. The hands are provided in a text format and a

Macros for candle-lora.

candle-lora-macro This library makes using candle-lora as simple as adding 2 macros to your model structs and calling a method! It is inspired by the

dm-jitaux is a Rust-based JIT compiler using modified auxtools, dmasm and Inkwell LLVM wrapper for boosting Byond DM performance without any hassle!

dm-jitaux is a Rust-based JIT compiler using modified auxtools, dmasm and Inkwell LLVM wrapper for boosting Byond DM performance without any hassle (such as rewriting/refactroing your DM code).

Comments
  • Mwt support in type names

    Mwt support in type names

    Is it feasible to add support the Mwt pattern within Type names, or is there some way to do this already that I've missed? For my specific use case I'd like to have the pattern work in the return type of a method (fn foo() -> ReturnTypeMwt). This is useful if you have mut and non mut variants of some guard type.

    If this is not already possible I'd be happy to take a stab at this and submit a pull request, though I may need some high level advice on how to approach it. My guess is that I should add a body to handle the case in the args.rs 'Type' match arm, and that I'd also need to handle the UpperCamelCase style of this use of Mwt?

    And lastly, thank you for this crate! I hope this gets more visibility within the community so that more people recognize that an official solution is missing in the language. I wonder how much copy pasted mut / non mut code there is in the standard lib or how they have handled this.

    opened by mmulli 3
Owner
null
Macros to make writing proc-macro crates easy

proc-easy Macros to make writing proc-macro crates easy. This crate provides mainly macros and supporting types and traits to reduce amount of boilerp

Zakarum 7 Jan 1, 2023
Jonathan Kelley 33 Dec 6, 2022
Attribute macro for implementing methods on both Foo and ArchivedFoo.

rkyv_impl Implement methods for Foo and ArchivedFoo in a single impl block. use rkyv::Archive; use rkyv_impl::*; use std::iter::Sum; #[derive(Archive

Duncan 5 Aug 6, 2023
A Rust proc-macro crate which derives functions to compile and parse back enums and structs to and from a bytecode representation

Bytecode A simple way to derive bytecode for you Enums and Structs. What is this This is a crate that provides a proc macro which will derive bytecode

null 4 Sep 3, 2022
secmem-proc is a crate designed to harden a process against low-privileged attackers running on the same system trying to obtain secret memory contents of the current process.

secmem-proc is a crate designed to harden a process against low-privileged attackers running on the same system trying to obtain secret memory contents of the current process. More specifically, the crate disables core dumps and tries to disable tracing on unix-like OSes.

null 3 Dec 19, 2022
Example of structuring a proc macro crate for testability

testing-proc-macros Example of structuring a proc macro crate for testability. See accompanying blog post for details. License Licensed under either o

Ferrous Systems 12 Dec 11, 2022
twilight-interactions is a set of macros and utilities to work with Discord Interactions using twilight.

Twilight interactions twilight-interactions is a set of macros and utilities to work with Discord Interactions using twilight. Note: This crate is not

null 24 Dec 26, 2022
A library for parsing and generating ESP-IDF partition tables

esp-idf-part A library for parsing and generating ESP-IDF partition tables. Supports parsing from and generating to both CSV and binary formats. This

esp-rs 5 Nov 16, 2022
Cassette A simple, single-future, non-blocking executor intended for building state machines.

Cassette A simple, single-future, non-blocking executor intended for building state machines. Designed to be no-std and embedded friendly. This execut

James Munns 50 Jan 2, 2023
Cross-platform GUI written in Rust using ADB to debloat non-rooted android devices

Cross-platform GUI written in Rust using ADB to debloat non-rooted android devices. Improve your privacy, the security and battery life of your device.

w1nst0n 6.8k Jan 2, 2023