I've been tyring to define a different function for eacu unsigned type in Rust, and of course they have to have different names because Rust can't pick the right one by signature. So I'm trying to give them different names but I was unable to do so:
macro_rules! define_my_function {
($t:ty, $f_name:ident) => {
pub fn concat_idents!(my_function_, $ty)()(
stream: &mut T
) -> Result<$ty, ParseError> {
todo!()
}
}
}
define_my_function!(u8, name_for_u8);
Compiling playground v0.0.1 (/playground)
error: expected one of `(` or `<`, found `!`
--> src/lib.rs:3:29
|
3 | pub fn concat_idents!(my_function_, $ty)()(
| ^ expected one of `(` or `<`
...
11 | define_my_function!(u8, name_for_u8);
| ------------------------------------- in this macro invocation
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=9ccea3ddcc75bb2cfab14c52df7bc24f
It looks like this is impossible in Rust. What can I do then? Also I don't want to use concat_idents as it's nightly only
You can use the paste crate, that internally uses a procedural macro to create new identifiers:
macro_rules! define_my_function {
($t:ty) => {
::paste::paste! {
pub fn [<my_function_ $t>]() {
println!(stringify!($t));
}
}
}
}
define_my_function!(u8);
define_my_function!(u16);
fn main() {
my_function_u8();
my_function_u16();
}
Playground
Note that I had to slightly change the code you gave because it wasn't compiling.
From the documentation for std::concat_idents:
Also, as a general rule, macros are only allowed in item, statement or expression position. That means while you may use this macro for referring to existing variables, functions or modules etc, you cannot define a new one with it.
Unfortunately, it seems that at the time this is possible using a procedural macro, or by manually copy-pasting for each numeric type.
Related
I'm implementing a crate that has the purpose of wrapping some user code and make sure it is called in a certain context. I can summarize my workspace this way:
A "user_code" crate:
This contains a single "user" function, which I would have no control over. Notice that it returns a u32.
pub fn addition(a: u32, b: u32) -> u32 {
a + b
}
A "wrapper" crate:
This contains a macro_rules. This macro creates some modules and a caller function, which will call the user code seen above. This is a highly simplified version. Notice that the return value of addition is ignored.
#[macro_export]
macro_rules! macro_wrapper {
() => {
pub mod my_user_code {
use wrapper::*;
use user_code::*;
pub mod addition {
use wrapper::*;
use super::*;
pub fn caller(a: u32, b: u32) {
addition(a, b);
}
}
}
};
}
An "example" crate:
Here I simply call the wrapper macro, in order to form the modules and functions I need and then use in the main function.
#![deny(unused_results)]
use wrapper::macro_wrapper;
macro_wrapper! {
}
fn main() {
my_user_code::addition::caller(2,3);
println!("Hello, world!");
}
This might seem like super strange code (it is) but it's a very simplified example of a legit project :)
My goal now is to make sure that this code doesn't compile because the return value of addition is not used - it was forgotten. The lint #![deny(unused_results)] seems to achieve exactly what I want.
However, this works only if, instead of use wrapper::macro_wrapper; I have the macro directly defined in the main.rs. In that case I get:
error: unused result of type `u32`
--> example\src\main.rs:14:21
|
14 | addition(a, b);
| ^^^^^^^^^^^^^^^
...
21 | / macro_wrapper! {
22 | |
23 | | }
| |_- in this macro invocation
|
note: the lint level is defined here
--> example\src\main.rs:1:9
|
1 | #![deny(unused_results)]
| ^^^^^^^^^^^^^^
= note: this error originates in the macro `macro_wrapper` (in Nightly builds, run with -Z macro-backtrace for more info)
If the macro is defined in its crate "wrapper", the code compiles and runs just fine without any error. This is not the result I expect.
What am I missing here? Thank you in advance for reading!
The unused_results lint doesn't get fired for macro expansions for external macros. This is by design, because this lint can be very noisy, and you can't control the expansion of external macros.
If you need that, your best bet is to mark your function as #[must_use], then #![deny(unused_must_use)]. Because the unused_must_use lint does get fired for external macros, it will work.
If you can't control the function definition, a neat trick you can use is to define a dummy function whom only job is to trigger the lint:
#[doc(hidden)]
#[must_use]
pub fn must_use<T>(v: T) -> T { v }
// In the macro...
pub fn caller(a: u32, b: u32) {
$crate::must_use(addition(a, b));
}
I need to use an attribute of the generic type of a struct in a macro.
A slightly contrived but minimal example would be if I wanted to implement a method for a generic struct, that returned the minimum value of its generic type.
struct Barn<T> {
hay: T
}
macro_rules! impl_min_hay{
($barntype:ident) => {
impl $barntype {
fn min_hay(&self) -> ????T {
????T::MIN
}
}
}
}
type SmallBarn = Barn<i8>;
type BigBarn = Barn<i64>;
impl_min_hay!(SmallBarn);
impl_min_hay!(BigBarn);
fn main() {
let barn = SmallBarn { hay: 5 };
println!("{}", barn.min_hay());
}
How would I resolve from SmallBarn to get the generic type and thus it's MIN attribute?
The actual problem I am trying to solve is a change to this macro. The macro is applied to, among others, BooleanChunked, which is defined as:
pub type BooleanChunked = ChunkedArray<BooleanType>
And I need to use an attribute of BooleanType
The only general solution I can think of is to define a trait that allows you to get at the type parameter (the syntax for this is <Type as Trait>::AssociatedType):
trait HasHayType {
type HayType;
}
impl<T> HasHayType for Barn<T> {
type HayType = T;
}
macro_rules! impl_min_hay{
($barntype:ident) => {
impl $barntype {
fn min_hay(&self) -> <$barntype as HasHayType>::HayType {
<$barntype as HasHayType>::HayType::MIN
}
}
}
}
Here's the complete program on play.rust-lang.org.
That said, once you have a trait, you don't really need the macro – you can just implement min_hay on the trait (this example makes use of the widely used num-traits crate, because this approach needs a trait for "things that have minimum values"):
use num_traits::Bounded;
trait HasHayType {
type HayType: Bounded;
fn min_hay(&self) -> Self::HayType;
}
impl<T: Bounded> HasHayType for Barn<T> {
type HayType = T;
fn min_hay(&self) -> T {
T::min_value()
}
}
And here's what that looks like as a complete program.
(And of course, once you've done that too, you don't really need the separate trait either: you can inline the definition of HasHayType into Barn, using a where clause if you want to be able to handle Barns with non-numerical hay types in addition to the ones where you'd use the macro. Presumably, though, the actual situation you have is more complex than the cut-down example you used for the question, so I gave the more complex versions in case the simplified versions wouldn't work.)
As a side note, min_hay doesn't actually need the &self parameter here; you could remove it, in order to be able to learn the minimum amount of hay without needing a barn to put it in.
I am trying to write a macro that generates a struct in Rust. This macro will add different Serde attributes to struct fields based on the type of field. This is the final goal.
For now, I'm simply trying to write a macro that uses another macro for generating a recursive code.
This is what the code looks like:
macro_rules! f_list {
($fname: ident, $ftype: ty) => {
pub $fname: $ftype,
}
}
macro_rules! mk_str {
($sname: ident; $($fname: ident: $ftype: ty,)+) => {
#[derive(Debug, Clone)]
pub struct $sname {
$(
f_list!($fname, $ftype)
)+
}
}
}
mk_str! {
Yo;
name: String,
}
fn main() {
println!("{:?}", Yo { name: "yo".to_string() })
}
This code on running gives below error, which I'm not able to understand.
error: expected `:`, found `!`
--> src/main.rs:12:23
|
12 | f_list!($fname, $ftype);
| ^ expected `:`
What's wrong here?
Here's a playground link
When writing declarative macros (macro_rules!), it's important to understand that the output of a macro must be a pattern, a statement, an expression, an item or an impl. Effectively, you should think of the output as something that can stand alone, syntactically speaking.
In your code, the macro f_list, if it worked, would output code like
name1: type1,
name2: type2,
name3: type3,
While this could be part of a declaration for a struct, it is not itself something that can stand alone.
Why is the error in the other macro then? mk_str successfully expands to
#[derive(Debug, Clone)]
pub struct Yo {
f_list!(name, String)
}
However, the parser doesn't expect a macro inside of a struct declaration. The inside of a struct isn't a pattern, a statement, an expression, an item or an impl. Thus, when it sees the !, it gives up and reports an error.
How can you fix this? In this particular example, f_list is fairly redundant. You could simply replace f_list!($fname, $ftype) with pub $fname: $ftype, in mk_str and it'll work as written. If this doesn't work for your goal, take a look at The Little Book of Rust Macros. It has some patterns for doing very complicated things with macros. Most of the information in this answer came from the section "Macros in the AST".
macro_rules! call_on_self {
($F:ident) => {
self.$F()
}
}
struct F;
impl F {
fn dummy(&self) {}
fn test(&self) {
call_on_self!(dummy);
}
}
The above doesn't work (Playground):
error[E0424]: expected value, found module `self`
--> src/lib.rs:3:9
|
3 | self.$F()
| ^^^^ `self` value is a keyword only available in methods with `self` parameter
...
11 | call_on_self!(dummy);
| --------------------- in this macro invocation
I don't understand why this is not working: the macro is invoked in the method where self is available! Is this somehow possible? Should I pass self into the macro because otherwise the macro cannot resolve self?
I'm using rustc 1.19.0-nightly.
Rust macros are not just text replacement. Instead, there are a couple of important differences, one of which is "macro hygiene". With this, identifiers inside the macro don't interfere with identifiers outside which does prevent a couple of bugs that commonly happen with macro systems like C's.
As a consequence, a macro can only access identifiers that are:
passed explicitly, or
are in scope when the macro is defined.
While it might seem like an unnecessary restriction at first, it actually helps with the readability of code. Otherwise, it's "spooky action at a distance". It's more or less the same reasoning why passing references to a variable into a function is done via some_fn(&mut foo) in Rust and is not implicit like in C++ (some_fn(foo)): it's clearer how a variable is used by a function at the call site.
This means we have two ways to fix your problem. The standard solution is to pass in self to the macro:
macro_rules! call_on_self {
($self:ident, $F:ident) => {
$self.$F()
};
}
struct F;
impl F {
fn dummy(&self) {}
fn test(&self) {
call_on_self!(self, dummy);
}
}
If you only need to use the macro inside the test method, you can define the macro inside that method. Then, self is already in scope when the macro is defined, so it works without passing self:
struct F;
impl F {
fn dummy(&self) {}
fn test(&self) {
macro_rules! call_on_self {
($F:ident) => {
self.$F()
};
}
call_on_self!(dummy);
}
}
There's also fun combinations of the two, like defining a macro that takes self explicitly and another macro defined inside the function to capture self:
macro_rules! call_on_self_outer {
($self:ident, $F:ident) => {
$self.$F()
};
}
struct F;
impl F {
fn dummy(&self) {}
fn test(&self) {
macro_rules! call_on_self {
($F:ident) => {
call_on_self_outer!(self, $F);
};
}
call_on_self!(dummy);
}
}
`
I can build structs and enums using macros but not traits. Is this a bug or something about how traits work that I am missing? Here is a simple example that fails to build:
macro_rules! fun{
() => { fn hello(); }
}
macro_rules! full_fun{
() => { fn hello(){} }
}
// Fails with:
// <anon>:13:8: 13:11 error: expected one of `const`, `extern`, `fn`, `type`, or `unsafe`, found `fun`
// <anon>:13 fun!();
macro_rules! trait_macro{
($name:ident) => {
pub trait $name {
fun!();
}
};
}
macro_rules! struct_macro{
($name:ident) => {
pub struct $name;
impl $name {
full_fun!();
}
};
}
// I can add functions to a Impl
struct_macro!{Monster}
// But I cannot add functions to a trait
trait_macro!{Monster}
fn main() {
}
According to the Rust documentation on macros, a macro can be expanded as:
zero or more items
zero or more methods,
an expression,
a statement, or
a pattern.
Your full_fun becomes a method, but I think a declaration inside a trait doesn't count. (I haven't found an exact reference, though).
Even if it were, it wouldn't help: due to the macro hygiene rules, the hello defined couldn't be referenced elsewhere, as it would effectively be a unique identifier different from any other - ie your fun!() macro would not be declaring the same function as is implemented by full_fun!().
Macro inside trait expansion is currently not supported. The relevant AST code:
pub enum TraitItemKind {
Const(P<Ty>, Option<P<Expr>>),
Method(MethodSig, Option<P<Block>>),
Type(TyParamBounds, Option<P<Ty>>),
}
As the error message states only one of const, extern, fn, type, or unsafe are expected as next token in a Trait.