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);
}
}
`
Related
How can I expose a struct generated from the quote macro in my derive macro without having to introduce a struct name out of the blue in my usage file (due to macro expansion)?
To illustrate the point, currently, my code looks something like this:
// "/my_derive/lib.rs"
// inside a derive macro function
let tokens = quote! {
struct MyDeriveMacroInternalStruct {
variant: #ident_name,
// other stuff ...
}
impl #ident_name {
pub fn something() -> Vec<MyDeriveMacroInternalStruct> {
vec![MyDeriveMacroInternalStruct { variant: #ident_name::#variant_name, /*...*/ }, /*...*/]
}
}
};
tokens.into()
The usage of my code would look something like this:
use my_derive::MyDerive;
#[derive(MyDerive)]
enum Something {
A,
B,
C,
}
fn process_data() -> Vec<MyDeriveMacroInternalStruct> { // having to write that struct name that came out of nowhere bothers me
Something::something()
}
fn main() {
let result = process_data();
// do stuff...
}
This is a condensed version of my actual code (process_data is in another file). To reiterate my question in light of the example, how can I access the struct without having it randomly appear out of nowhere (due to macro expansion)? To me the code unchanged is hard to understand, read, and change.
I would like to be able to do something like this:
use my_derive::{MyDerive, MyDeriveStruct};
#[derive(MyDerive)]
enum Something {
A,
B,
C,
}
fn process_data() -> Vec<MyDeriveStruct> { // importing the struct instead of magically appearing
Something::something()
}
fn main() {
let result = process_data();
// do stuff...
}
Obviously the idea seems quite stupid, but there has to be a way around it (an arbitrary struct definition). If what I imagined isn't possible, is there some way to be more clear about where the random struct came from?
Actually I thought of something better. Your derive should probably be associated with a trait of the same name.
Add an associated type to your trait:
trait MyDerive {
type Output;
...
}
Then set the associated type when you impl the trait:
struct MyDeriveMacroInternalStruct {
variant: #ident_name,
// other stuff ...
}
impl MyDerive for #ident_name {
type Output = MyDeriveMacroInternalStruct;
pub fn something() -> Vec<MyDeriveMacroInternalStruct> {
vec![MyDeriveMacroInternalStruct { variant: #ident_name::#variant_name, /*...*/ }, /*...*/]
}
}
Then you can refer to that associated type in return position or wherever:
use my_derive::MyDerive;
#[derive(MyDerive)]
enum Something {
A,
B,
C,
}
fn process_data() -> Vec<<Something as MyDerive>::Output> {
Something::something()
}
fn main() {
let result = process_data();
// do stuff...
}
Note: the convention is for #[derive(Trait)] to correspond to an impl for the given Trait, but your proc macro crate can't export a trait directly for importing in your library code.
So generally the solution is to have two crates:
my-trait is the "library" crate which contains the MyTrait trait definition
my-trait-derive is the proc-macro crate which contains the derive macro code
my-trait has my-trait-derive as a direct dependency, and re-exports the proc macro from it:
// my-trait lib.rs
pub use my_trait_derive::MyTrait;
// macro and trait names can overlap as they're
// treated as different item kinds
pub trait MyTrait {
type Output;
fn something();
}
see how clap does it here (they also re-export the whole clap_derive)
Then a user can use your proc macro + trait like this:
use my_trait::MyTrait;
#[derive(MyTrait)]
enum Something {}
fn process_data() -> Vec<<Something as MyTrait>::Output> {
Something::something()
}
Older Answer
What I would do is create a trait MyDeriveOutput or something with whatever stuff you want exposed from MyDeriveMacroInternalStruct:
trait MyDeriveOutput {
fn variant() ...
}
And then generate an impl for each internal struct you create:
struct MyDeriveMacroInternalStruct {
variant: #ident_name,
// other stuff ...
}
impl MyDeriveOutput for MyDeriveMacroInternalStruct {
// whatever
}
Then you can expose the trait and require it to be imported and used with impl Trait in return position:
use my_derive::{MyDerive, MyDeriveOutput};
#[derive(MyDerive)]
enum Something {
A,
B,
C,
}
fn process_data() -> Vec<impl MyDeriveOutput> {
Something::something()
}
fn main() {
let result = process_data();
// do stuff...
}
How do I get over something like this:
struct Test {
foo: Option<fn()>
}
impl Test {
fn new(&mut self) {
self.foo = Option::Some(self.a);
}
fn a(&self) { /* can use Test */ }
}
I get this error:
error: attempted to take value of method `a` on type `&mut Test`
--> src/main.rs:7:36
|
7 | self.foo = Option::Some(self.a);
| ^
|
= help: maybe a `()` to call it is missing? If not, try an anonymous function
How do I pass a function pointer from a trait? Similar to what would happen in this case:
impl Test {
fn new(&mut self) {
self.foo = Option::Some(a);
}
}
fn a() { /* can't use Test */ }
What you're trying to do here is get a function pointer from a (to use Python terminology here, since Rust doesn't have a word for this) bound method. You can't.
Firstly, because Rust doesn't have a concept of "bound" methods; that is, you can't refer to a method with the invocant (the thing on the left of the .) already bound in place. If you want to construct a callable which approximates this, you'd use a closure; i.e. || self.a().
However, this still wouldn't work because closures aren't function pointers. There is no "base type" for callable things like in some other languages. Function pointers are a single, specific kind of callable; closures are completely different. Instead, there are traits which (when implemented) make a type callable. They are Fn, FnMut, and FnOnce. Because they are traits, you can't use them as types, and must instead use them from behind some layer of indirection, such as Box<FnOnce()> or &mut FnMut(i32) -> String.
Now, you could change Test to store an Option<Box<Fn()>> instead, but that still wouldn't help. That's because of the other, other problem: you're trying to store a reference to the struct inside of itself. This is not going to work well. If you manage to do this, you effectively render the Test value permanently unusable. More likely is that the compiler just won't let you get that far.
Aside: you can do it, but not without resorting to reference counting and dynamic borrow checking, which is out of scope here.
So the answer to your question as-asked is: you don't.
Let's change the question: instead of trying to crowbar a self-referential closure in, we can instead store a callable that doesn't attempt to capture the invocant at all.
struct Test {
foo: Option<Box<Fn(&Test)>>,
}
impl Test {
fn new() -> Test {
Test {
foo: Option::Some(Box::new(Self::a)),
}
}
fn a(&self) { /* can use Test */ }
fn invoke(&self) {
if let Some(f) = self.foo.as_ref() {
f(self);
}
}
}
fn main() {
let t = Test::new();
t.invoke();
}
The callable being stored is now a function that takes the invocant explicitly, side-stepping the issues with cyclic references. We can use this to store Test::a directly, by referring to it as a free function. Also note that because Test is the implementation type, I can also refer to it as Self.
Aside: I've also corrected your Test::new function. Rust doesn't have constructors, just functions that return values like any other.
If you're confident you will never want to store a closure in foo, you can replace Box<Fn(&Test)> with fn(&Test) instead. This limits you to function pointers, but avoids the extra allocation.
If you haven't already, I strongly urge you to read the Rust Book.
There are few mistakes with your code. new function (by the convention) should not take self reference, since it is expected to create Self type.
But the real issue is, Test::foo expecting a function type fn(), but Test::a's type is fn(&Test) == fn a(&self) if you change the type of foo to fn(&Test) it will work. Also you need to use function name with the trait name instead of self. Instead of assigning to self.a you should assign Test::a.
Here is the working version:
extern crate chrono;
struct Test {
foo: Option<fn(&Test)>
}
impl Test {
fn new() -> Test {
Test {
foo: Some(Test::a)
}
}
fn a(&self) {
println!("a run!");
}
}
fn main() {
let test = Test::new();
test.foo.unwrap()(&test);
}
Also if you gonna assign a field in new() function, and the value must always set, then there is no need to use Option instead it can be like that:
extern crate chrono;
struct Test {
foo: fn(&Test)
}
impl Test {
fn new() -> Test {
Test {
foo: Test::a
}
}
fn a(&self) {
println!("a run!");
}
}
fn main() {
let test = Test::new();
(test.foo)(&test); // Make sure the paranthesis are there
}
fn a() {} seems to satisfy a parsing rule that expected a fn, then some other stuff. items should be able to be function definitions, right? So they should work, right?
macro_rules! multi_impl {
(for $base:ty :
$($t:ty {
$($i:item);*
}),+) =>
{
$(
impl $t for $base
{
$( $i )*
}
)+
}
}
trait A {
fn a();
}
trait B {
fn b();
}
struct S;
multi_impl! {
for S:
A {
fn a() {}
}, B {
fn b() {}
}
}
fn main() {
S::a();
S::b();
}
playground
The error in question:
error: expected one of `const`, `default`, `extern`, `fn`, `pub`, `type`, `unsafe`, or `}`, found `fn a() { }`
--> <anon>:11:20
|
11 | $( $i )*
| ^^
Making it $( fn $i)* only changes the error to complain about expecting an identifier after the fn, which makes sense, but the initial error does not (at least to me).
Is there a difference to the parser about code that is in the source vs code that's placed into the source by a macro?
The problem isn't that a fn isn't an item, it's that the body of an impl does not contain items. It contains "impl items". What it's complaining about is you trying to put a square-shaped block into a round hole, not that the block is the wrong colour.
Yes, those are two different things. No, you can't capture "impl items" in a macro. No, you can't turn an item into an impl item. Because macros capture AST nodes, not the tokens. Well, methods can have a self argument and regular function's don't. I don't know, presumably it seemed like a good idea at the time.
Putting the hypothetical back-and-forth aside, the solution in this case is to not bother trying to match anything in particular and just match anything.
macro_rules! multi_impl
{
(for $base:ty :
$($t:ty {
$($body:tt)*
}),+) =>
{
$(
impl $t for $base
{
$($body)*
}
)+
}
}
I'm trying to create a macro that generates a struct that provides a set of methods that are passed into the macro. For example, calling:
create_impl!(StructName, fn foo() -> u32 { return 432 })
should generate an empty struct StructName that provides the method foo().
My initial attempt at this uses the item macro arg type. However, when I try and use an item in the rule, I get the following compiler error:
error: expected one of `const`, `default`, `extern`, `fn`, `pub`, `type`, `unsafe`, or `}`, found `fn foo() -> u32 { return 42; }`
--> src/lib.rs:40:13
|
40 | $($function)*
| ^^^^^^^^^
Is it possible to use item arguments to define methods in generated structs this way? Is there something I'm missing?
Here's the full macro I've defined:
macro_rules! create_impl {
($struct_name:ident, $($function:item),*) => {
struct $struct_name {
}
impl $struct_name {
// This is the part that fails.
$($function)*
}
};
}
The short answer is "no, you can't use the item matcher for a method".
According to the reference, items are the top level things in a crate or module, so functions, types, and so on. While a struct or impl block is an item, the things inside them aren't. Even though syntactically, a method definition looks identical to a top level function, that doesn't make it an item.
The way Rust's macro system works is that once a fragment has been parsed as an item, e.g. using $foo:item, it's then forever an item; it's split back into tokens for reparsing once the macro is expanded.
The result of this is that $foo:item can only be in the macro's output in item position, which generally means top-level.
There are a couple of alternatives.
The simplest is to use the good old tt (token tree) matcher. A token tree is either a non-bracket token or a sequence of tokens surrounded by balanced brackets; so $(foo:tt)* matches anything. However, that means it will gobble up commas too, so it's easier to just add braces around each item:
macro_rules! create_impl {
($struct_name:ident, $({ $($function:tt)* }),*) => {
struct $struct_name {
}
impl $struct_name {
$($($function)*)*
}
};
}
Then you have to use it with the extra braces:
create_impl!(StructName, { fn foo() -> u32 { return 432 } }, { fn bar() -> u32 { return 765 } });
You can also just match the syntax you want directly, rather than delegating to the item matcher:
macro_rules! create_impl2 {
($struct_name:ident, $(fn $fname:ident($($arg:tt)*) -> $t:ty $body:block),*) => {
struct $struct_name {
}
impl $struct_name {
$(fn $fname($($arg)*) -> $t $body)*
}
}
}
Of course since it's explicit, that means that if you want to support functions without a return type, you need to add another case to your macro.
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.