I am building a crate which has the feature foo. This crate has a macro bar! that does subtly different things based on whether foo is set.
I could duplicate the entire macro:
#[cfg(feature = "foo")]
macro_rules! bar {
// Lots of rules...
( A ) => {
B
}
}
#[cfg(not(feature = "foo"))]
macro_rules! bar {
// Lots of rules...
( A ) => {
C
}
}
That's a lot of error-prone duplication. Two approaches that don't work:
We can't move the cfg inside the macro, because then it will expand in the crate user's scope, which does not have feature foo appropriately set.
We can't use a #[doc(hidden)] #[macro_export] macro_rules! bar_priv_impl__ helper macro and use #[cfg] on the helper macro since Rust 1.30, because users can now request use mycrate::bar; to only import the bar! macro, giving errors about how bar_priv_impl__! is not defined.
Is there any way that's better than full macro duplication? It gets really bad if you have N features you're testing on, as you need 2n duplications.
since Rust 1.30 [...] because users can now [...] only import the bar! macro
Actually, this solution is only possible in Rust 1.30 because of the ability to import macros like normal. Remember that your macro can also have use statements!:
#[macro_export]
macro_rules! bar {
($val:expr) => {{
use $crate::__bar_foo;
__bar_foo!($val)
}}
}
#[cfg(feature = "foo")]
#[macro_export]
macro_rules! __bar_foo {
($val:expr) => ($val + 1)
}
#[cfg(not(feature = "foo"))]
#[macro_export]
macro_rules! __bar_foo {
($val:expr) => ($val - 1)
}
You can also fully-qualify your helper macro invocation:
#[macro_export]
macro_rules! bar {
($val:expr) => ($crate::__bar_foo!($val))
}
Related
I'm trying to match the trait bounds for generic types:
macro_rules! test {
(
where $(
$bounded_type:ident: $( $bound:tt )++,
)+
) => {
// Dummy expansion for test:
struct Foo<T, U>
where $(
$bounded_type : $( $bound )++,
)+
{
t: T,
u: U
}
}
}
test! {
where
T: PartialEq + Clone,
U: PartialEq,
}
fn main() {}
Unfortunately, if I understand well, the only way to match a trait is a tt fragment, but this fragment can match almost anything, so whatever I do, I get an error:
error: local ambiguity: multiple parsing options: built-in NTs tt ('bound') or 1 other option.
How can I match this piece of code?
Note that I do not need something very elegant (I do not need it for plublic users) but of course, the more elegant, the better.
Your best bet is to read the source code for the parse-generics-shim crate; it's a bit old, but should hopefully still work. This is way too convoluted to explain in a Stack Overflow question, since it would basically involve copy+pasting the source of that crate into the answer.
The easier approach is to just not parse actual Rust syntax, and use something the macro parser can handle, like wrapping the constraints in a group (like { ... }).
I was able to get this to match by splitting the first bound from the rest.
macro_rules! test {
(
where $(
$bounded_type:ident: $bound:tt $(+ $others:tt )*,
)+
) => {
// Dummy expansion for test:
struct Foo<T, U>
where $(
$bounded_type : $bound $(+ $others)*,
)+
{
t: T,
u: U
}
}
}
However, this isn't going to work if the traits have parameters.
Since around 2016 with the closing of this issue, you could use the path macro type to match TypePaths.
For example, from my own code:
($name:ident<$($l:lifetime, )*$($x:ident : $xt:path),+>($s:ident$(, $a:ident: $t:ty)*) -> $ret:ty => $body:block) => {
}
This question already has answers here:
Why use curly braces over parentheses?
(3 answers)
Closed 4 years ago.
In Scala, we can have:
println { "Hello, world!" }
And from the book 'Programming in Scala':
The purpose of this ability to substitute curly braces for parentheses for
passing in one argument is to enable client programmers to write function
literals between curly braces. This can make a method call feel more like a
control abstraction.
What does this statement mean?
This is syntactic sugar just for look and feel. When a function takes a function as argument like in
def doWith[A, B](todo: A => B): B = ???
You would normally have to call it like
doWith( input => ... )
// or even
doWith({ input => ... })
In scala it is allowed to replace parenthesis with with curlies, so
doWith { input =>
...
}
Has the look and feel of a control structure like
if (...) {
...
}
Imho, that makes calling higher order functions like 'map' or 'collect' much more readable:
someCollection.map { elem =>
...
...
}
which is essentially the same as
someCollection.map({ elem =>
...
...
})
with less chars.
"Control abstractions" are e.g. if, while, etc. So you can write a function
def myIf[A](cond: Boolean)(ifTrue: => A)(ifFalse: => A): A =
if (cond) ifTrue else ifFalse
(if you aren't familiar with : => Type syntax, search for "by-name parameters") you can call it as
val absX = myIf(x < 0) { -x } { x }
and it looks very similar to normal if calls. Of course, this is much more useful when the function you write is more different from the existing control structures.
In addition to (regular) functions and by-name arguments, braces also help with partial functions:
processList(l) {
case Nil => ...
case h :: t => ...
}
and sequences of expressions:
doSomething {
thing1;
thing2
}
Note that (thing1; thing2) is not a valid expression in Scala like it would be in, say, ML.
Actually what I have noticed the difference between the curly braces{} and parenthesis() is that you can write multiple lines in the curly braces. While in parenthesis() you can’t write more then one line for example.
val x :[List[Int]]=List(1,2,3,4,5,6,7,8)
x.map(y=> y*5) //it will work fine
x.map(y=>
case temp:Int=>println(temp)
case _ => println(“NOT Int”)) //it will not work
x.map{y=>
case temp:Int=>println(temp)
case _ => println(“NOT Int”)} //it willwork
So we can say it’s just the synthetic sugar to allow developer to write more then none line without ; that’s it it may have some other reason too.
Types passed directly to macros pattern match the way you would expect, but if they're passed through another macro as ty, they stop matching:
macro_rules! mrtype {
( bool ) => ("b");
( i32 ) => ("i");
( f64 ) => ("f");
( &str ) => ("z");
( $_t:ty ) => ("o");
}
macro_rules! around {
( $t:ty ) => (mrtype!($t));
}
fn main() {
println!("{}{}{}", mrtype!(i32), around!(i32), around!(&str));
}
This prints ioo instead of iiz.
Passing tts instead of tys works, but if you have &str you need 2 tts, making everything unnecessarily complicated.
This doesn't work and cannot be made to work.
To summarise the Captures and Expansion Redux chapter of The Little Book of Rust Macros: the problem is that with the exception of tt and ident captures, macro_rules! is completely unable to destructure or examine captured tokens. Once you capture something as ty, it irrevocably becomes a black box to macro_rules!.
To put it another way: &str is not a type, as far as macro_rules! is concerned: it's two tokens, & and str. When you capture and then substitute &str as ty, though, it becomes a single "meta token": the type &str. The two are no longer the same, and as such don't match.
If you intend to later match against or destructure tokens, you must capture them as tts or idents (if possible). In this specific case, you could rewrite the rule for around to instead be ($($t:tt)*) => (mrtype!($($t)*));, which preserves the original token sequence.
This code:
#![feature(macro_rules)]
macro_rules! new(
($my_type:ty) => ( $my_type::new() );
)
struct Foo {
blah: int
}
impl Foo {
fn new() -> Foo {
return Foo { blah: 0 }
}
}
fn main() {
let my_foo = new!(Foo);
println!("Foo's value: {}", my_foo.blah);
}
Looks good enough, but it fails with this error:
test.rs:4:25: 4:32 error: unexpected token: `Foo`
test.rs:4 ($my_type:ty) => ( $my_type::new() );
^~~~~~~
If I go into the macro and replace $my_type with Foo it compiles and runs just fine, so Foo is clearly valid in that position. Unless Foo comes from macro substitution, apparently.
If I run rustc test.rs --pretty expanded, it doesn't show me the expanded macro. It just gives me the same error message. I suspect this means it's generating the message before it expands the macro, but it might just be that it doesn't show me anything unless the compile succeeds. Though that would severely limit the usefulness of --pretty expanded.
Based on other experiments, I can use the macro type arguments in basically every other place one would expect a type to work. You just can't call static functions on them. This seems like a rather arbitrary restriction, and the error message is certainly not helpful.
Why does this restriction exist? And is there a way around it?
The Foo::bar() syntax is creating the path Foo::bar and then calling that function, and only works with valid paths, it doesn't work with arbitrary types, e.g. (u8, i8)::bar() doesn't work. You can use the ident macro non-terminal, which takes a single identifier and can be used whereever an identifier is valid, including inside a path
#![feature(macro_rules)]
macro_rules! new(
($my_type: ident) => ( $my_type::new() );
)
struct Foo {
blah: int
}
impl Foo {
fn new() -> Foo {
return Foo { blah: 0 }
}
}
fn main() {
let my_foo = new!(Foo);
println!("Foo's value: {}", my_foo.blah);
}
UFCS offers calling such methods on arbitrary types, via the syntax <Type>::new() and so, when that is implemented, replacing your current macro with
macro_rules! new(
($my_type: ty) => ( <$my_type>::new() );
)
should work too.
I am trying to write a print function for a binary tree and here is what I have so far:
impl TreeNode {
fn print(&self) {
self.print(0);
}
fn print(&self, level: u8) {
for _i in range(0,level) {
print!("\t");
}
match self.data {
Some(x) => println!("{}",x),
None => ()
};
match self.left {
Some(ref x) => x.print(level+1),
None => ()
};
match self.right {
Some(ref x) => x.print(level+1),
None => ()
};
}
}
I am getting the error: duplicate definition of value print. So I was wondering if there is a way to create functions with the same name but different arguments. Alternatively optional parameters would solve this problem, but I don't think that is possible at the moment (at least I couldn't find it via a Google search).
So, what is the best way to do this? Renaming the second print function works but looks ugly and requires you to remember more than one function name if I want to (for this example) print starting from the middle of the tree.
Rust does not have overloading, so it is impossible to have two functions or methods with the same name and with different sets of parameters.
However, it is sometimes possible to emulate overload with traits. This approach is likely inappropriate for your use case, but you can see how it is done in the standard library, where Path::new() constructor can be called with something resembling a vector of bytes:
Path::new("/a/b/c/d") // argument is &str
Path::new(b"/a/b/c/d") // argument is &[u8]
Path::new(Path::new("/a/b/c/d")) // argument is another Path
This is done via BytesContainer trait, and new() method is defined like this:
fn new<T: BytesContainer>(bytes: T) -> Path { ... }
Then this trait is implemented for all the types you want:
impl<'a> BytesContainer for &'a str { ... }
impl<'a> BytesContainer for &'a [u8] { ... }
impl BytesContainer for Path { ... }
// and more
This resembles overloading precisely because new() does exactly the same thing regardless of what kind of input it is provided; it is just a convenience thing which makes Path constructor more flexible. In the end new() just converts its argument to a byte slice. However, this does not allow you to have completely different functions with the same name.