Do Rust macros work inside trait definitions? - rust

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.

Related

Refer to generic type of struct in macro

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.

How do I get a function pointer from a trait in Rust?

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
}

How to call methods on self in macros?

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);
}
}
`

Why am I getting a "value must be known" error on tuple in a for-loop?

struct Plugin;
struct Blueprint<'a>(&'a ());
struct Shell<'a> {
plugins: Vec<(&'a Plugin, Vec<Blueprint<'a>>)>,
}
impl<'a> Shell<'a> {
fn find_blueprint(&self, name: &str) -> Option<Blueprint> {
for plugin_blueprints in self.plugins.as_ref() {
for blueprint in plugin_blueprints.1 {
if blueprint.name.to_string() == name {
return Some(blueprint);
}
}
}
None
}
}
fn main() {}
Generates this error:
error: the type of this value must be known in this context
--> src/main.rs:11:30
|
11 | for blueprint in plugin_blueprints.1 {
| ^^^^^^^^^^^^^^^^^^^
This confuses me because plugin_blueprints seems to be unambiguously of type (&'a Plugin, Vec<Blueprint<'a>>). I'm not sure what syntax (if any) I would use to specify the type in a for-loop. Turbo-fish ::< doesn't seem to work.
Because you are using as_ref, which is more generic than you want. The value of T cannot be inferred:
pub trait AsRef<T>
where T: ?Sized
{
fn as_ref(&self) -> &T;
}
The idiomatic way to iterate over this is
for plugin_blueprints in &self.plugins {}
The pretty nasty way of doing it is to use the turbofish on the trait, using the disambiguated function call syntax:
for plugin_blueprints in AsRef::<[(&'a Plugin, Vec<Blueprint<'a>>)]>::as_ref(&self.plugins) {
Your function will actually return a Option<&Blueprint> because you start with &self. You should also use self.plugins.iter() and plugin_blueprints.1.iter() to stop the ambiguity introduced by as_ref() and fix your lifetimes.
See here

Is it possible to use an item arg passed to a macro as a method?

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.

Resources