Why is a negative integer not a valid pattern in a macro? - macros

Let us consider a simple enum implementation with a static method that check whether a value has an associated value (the efficiency of the implementation is not to be regarded here):
enum Letter {
Alpha = -1,
A = 0,
B = 1,
C = 2,
}
impl Letter {
pub fn in_enum(value: isize) -> bool
{
match value {
-1 => true,
0 => true,
1 => true,
2 => true,
_ => false,
}
}
}
Now, let us write a macro for building enums with an equivalent in_enum method. The macro below was written with some guidance from the Serde guide for enum deserialization as numbers, in which matching for enum variant values also occurs.
macro_rules! my_enum {
($name:ident { $($variant:ident = $value:expr, )* }) => {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum $name {
$($variant = $value,)*
}
impl $name {
pub fn in_enum(value: isize) -> bool
{
match value {
$( $value => true, )*
_ => false,
}
}
}
}
}
my_enum!(Letter {
Alpha = -1,
A = 0,
B = 1,
C = 2,
});
Playground.
With version 1.18.0, the compiler won't accept the variant with a negative integer.
error: expected pattern, found `-1`
--> src/main.rs:13:24
|
13 | $( $value => true, )*
| ^^^^^^
This seems to happen regardless of how I write this pattern down in the macro, or whether I use i32 or isize for the value method parameter. Changing the fragment specifier of $value to pat is also out of the question: the compiler will refuse to build the enum, even without negative variant values.
error: expected expression, found `-1`
--> src/main.rs:5:26
|
5 | $($variant = $value,)*
| ^^^^^^
What's surprising about this is that it works without using macros, as well as when I discard the Alpha variant.
Why does this happen?

This is a bug in the compiler and is already fixed in the nightly version as of today (Jul 5, 2017), and released in stable version 1.20.0.

Related

Optional function arguments with no default value possible?

In Chapel, we can set the default value of function formal arguments easily, for example,
proc test( a = 1, b = 2.0, c = "hi" ) {
...
}
and call the function by using keywords also:
test( 10 ); // a = 10, b = 2.0, c = "hi"
test( b = 3.14 ); // a = 1, b = 3.14, c = "hi"
test( c = "yo" ); // a = 1, b = 2.0, c = "yo"
Here, I am wondering if it is possible to define a keyword argument that does not require a predefined default value. More specifically, I would like to write a function that can optionally receive an array depending on cases (e.g., to save intermediate data). Here, the only requirement is that I can check whether the actual argument is passed or not, and there is no need to give the default array value. I imagined something like
proc test( ..., optional d: [] real ) {
if present( d ) then ...;
}
or
proc test( ..., d: [] real = None ) {
if present( d ) then ...;
}
but was not able to find similar things. At the moment, my workaround is to give some dummy default value and check their properties to determine whether an actual argument is passed.
proc test( arr = empty2Dreal ) { ... } // where "empty2Dreal" is a pre-defined global array
or
proc test( arr = reshape( [0.0], {1..1,1..1} ) ) { ... } // some dummy array
}
However, I am wondering whether there might be a more elegant(?) or idiomatic(?) approach...
Edit
As suggested in the comment, it is also convenient to overload several functions to get different interfaces, but at some point I guess I need to pass some "dummy" object to the final (full-fledged) routine and ask the latter to see if the passed object is "dummy" or not... MWE is something like this:
const empty1Dint: [1..0] int;
proc test( x: real, arr: [] int )
{
writeln("test() with 2 args");
writeln(( x, arr ));
// here, I need to check whether the passed object is
// an actual array or not by some predefined rule
if arr.size > 0 then writeln("got a non-empty array");
}
proc test( x: real )
{
writeln("test() with 1 arg");
test( x = x, arr = empty1Dint );
}
var work = [1,2,3,4,5];
test( x = 1.0 );
writeln();
test( x = 1.0, arr = work );
which gives
test() with 1 arg
test() with 2 args
(1.0, )
test() with 2 args
(1.0, 1 2 3 4 5)
got a non-empty array
The corresponding default-value version is
const empty1Dint: [1..0] int;
proc test( x: real, arr: [] int = empty1Dint )
{
writeln("test() with 2 args");
writeln(( x, arr ));
if arr.size > 0 then writeln("got a non-empty array");
}
var work = [1,2,3,4,5];
test( x = 1.0 );
writeln();
test( x = 1.0, arr = work );
which gives
test() with 2 args
(1.0, )
test() with 2 args
(1.0, 1 2 3 4 5)
got a non-empty array
Although the above approach works for arrays, the rule needs to change depending on the type of objects used. So, I was wondering if there is some systematic way, e.g., to pass a "null pointer" or some unique global object to tell the final routine about the presence of the actual data. (But, as noted above, the above approach works for arrays).
Edit 2
Another approach may be simply to pass an additional flag for using the passed array (then there is no need to think much about the nature of the default object, so may be overall simpler...)
const empty1Dint: [1..0] int;
proc test( x: real, arr: [] int = empty1Dint, use_arr = false )
{
writeln( "x= ", x );
if use_arr {
writeln("working with the passed array...");
for i in 1..arr.size do arr[ i ] = i * 10;
}
}
test( x = 1.0 );
writeln();
var work: [1..5] int;
test( x = 2.0, arr = work, use_arr = true );
writeln( "work = ", work );
Edit 3
Following Option 3 in the answer, here is a modified version of my code using _void and void:
proc test( x: real, arr: ?T = _void )
{
writeln( "\ntest():" );
writeln( "x = ", x );
writeln( "arr = ", arr );
writeln( "arr.type = ", arr.type:string );
writeln( "T = ", T:string );
if arr.type != void {
writeln( "doing some checks" );
assert( isArray( arr ) );
}
if arr.type != void {
writeln( "writing arr" );
for i in 1..arr.size do arr[ i ] = i * 10;
}
}
// no optional arg
test( x = 1.0 );
// use an optional arg
var work: [1..5] int;
test( x = 2.0, arr = work );
writeln( "\nmain> work = ", work );
Result:
test():
x = 1.0
arr =
arr.type = void
T = void
test():
x = 2.0
arr = 0 0 0 0 0
arr.type = [domain(1,int(64),false)] int(64)
T = [domain(1,int(64),false)] int(64)
doing some checks
writing arr
main> work = 10 20 30 40 50
This answer discusses 3 answers:
The strategy discussed in the edit of the question.
A strategy using a Box type
A strategy using a generic function with a void default value
My favorite of these options is Option 3.
Option 1
proc test( x: real, arr: [] int = empty1Dint, use_arr = false ) strategy described in the question is reasonable, if a little verbose. The main drawback here is that you'd need more overloads of test if you didn't want the call sites to have to pass use_arr=true or use_arr=false. Here is a simple program that does that:
proc test(optional, hasOptional:bool) {
writeln("in test");
writeln(" optional is ", optional);
if hasOptional == false then
writeln(" note: default was used for optional");
}
proc test(optional) {
test(optional, hasOptional=true);
}
proc test() {
var emptyArray:[1..0] int;
test(emptyArray, hasOptional=false);
}
test();
test([1, 2, 3]);
Option 2
Another alternative is to create a class to store the optional argument data, and pass nil by default.
class Box {
var contents;
}
proc makeArray() {
var A:[1..2] int;
return A;
}
proc emptyBox() {
var A:[1..0] int;
var ret: owned Box(A.type) = nil;
return ret;
}
proc test( optional=emptyBox() ) {
writeln("in test with optional=", optional);
}
test();
test(new owned Box(makeArray()));
Here the main tricky part is that the array type returned by makeArray() and emptyBox() have to match. It'd be possible to use a type alias to have them refer to the same array type, but how exactly that would fit in depends on your application. Another problem with this approach is that it causes the array to be copied in the process of passing such an argument. And, one has to think about where the Box will be destroyed. Is test to hang on to the array value (e.g. storing it in a data structure) or just going to use it temporarily? This is set by the type returned by emptyBox in my example.
It's probably reasonable for the standard library to gain such a Box type but it doesn't have one now.
Option 3
My favorite solution to this problem is a third strategy altogether.
Chapel includes a value of void type called _void. The key is the declaration proc test( optional:?t=_void ). Here test is a generic function - the syntax argument:?t indicates that the argument can have a varied type (which will be available as t within the function). This is necessary to get a generic argument that also has a default value (otherwise the argument will have only the type inferred from the default value).
If no optional argument is provided, it will instantiate with optional having type void. Which makes sense as a way to not pass something. Technically it's not the same as checking if the default value was provided, but I think a call site like test(optional=_void) is reasonably clear at communicating that the value of optional should be ignored (since it's void).
Anyway here is the code:
proc test( optional:?t=_void ) {
writeln("in test");
writeln(" optional is ", optional);
if optional.type == void then
writeln(" note: default was used for optional");
}
test();
test([1, 2, 3]);

Is there a way to count with macros?

I want to create a macro that prints "Hello" a specified number of times. It's used like:
many_greetings!(3); // expands to three `println!("Hello");` statements
The naive way to create that macro is:
macro_rules! many_greetings {
($times:expr) => {{
println!("Hello");
many_greetings!($times - 1);
}};
(0) => ();
}
However, this doesn't work because the compiler does not evaluate expressions; $times - 1 isn't calculated, but fed as a new expression into the macro.
While the ordinary macro system does not enable you to repeat the macro expansion many times, there is no problem with using a for loop in the macro:
macro_rules! many_greetings {
($times:expr) => {{
for _ in 0..$times {
println!("Hello");
}
}};
}
If you really need to repeat the macro, you have to look into procedural macros/compiler plugins (which as of 1.4 are unstable, and a bit harder to write).
Edit: There are probably better ways of implementing this, but I've spent long enough on this for today, so here goes. repeat!, a macro that actually duplicates a block of code a number of times:
main.rs
#![feature(plugin)]
#![plugin(repeat)]
fn main() {
let mut n = 0;
repeat!{ 4 {
println!("hello {}", n);
n += 1;
}};
}
lib.rs
#![feature(plugin_registrar, rustc_private)]
extern crate syntax;
extern crate rustc;
use syntax::codemap::Span;
use syntax::ast::TokenTree;
use syntax::ext::base::{ExtCtxt, MacResult, MacEager, DummyResult};
use rustc::plugin::Registry;
use syntax::util::small_vector::SmallVector;
use syntax::ast::Lit_;
use std::error::Error;
fn expand_repeat(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult + 'static> {
let mut parser = cx.new_parser_from_tts(tts);
let times = match parser.parse_lit() {
Ok(lit) => match lit.node {
Lit_::LitInt(n, _) => n,
_ => {
cx.span_err(lit.span, "Expected literal integer");
return DummyResult::any(sp);
}
},
Err(e) => {
cx.span_err(sp, e.description());
return DummyResult::any(sp);
}
};
let res = parser.parse_block();
match res {
Ok(block) => {
let mut stmts = SmallVector::many(block.stmts.clone());
for _ in 1..times {
let rep_stmts = SmallVector::many(block.stmts.clone());
stmts.push_all(rep_stmts);
}
MacEager::stmts(stmts)
}
Err(e) => {
cx.span_err(sp, e.description());
DummyResult::any(sp)
}
}
}
#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
reg.register_macro("repeat", expand_repeat);
}
added to Cargo.toml
[lib]
name = "repeat"
plugin = true
Note that if we really don't want to do looping, but expanding at compile-time, we have to do things like requiring literal numbers. After all, we are not able to evaluate variables and function calls that reference other parts of the program at compile time.
As the other answers already said: no, you can't count like this with declarative macros (macro_rules!).
But you can implement the many_greetings! example as a procedural macro. procedural macros were stabilized a while ago, so the definition works on stable. However, we can't yet expand macros into statements on stable -- that's what the #![feature(proc_macro_hygiene)] is for.
This looks like a lot of code, but most code is just error handling, so it's not that complicated!
examples/main.rs
#![feature(proc_macro_hygiene)]
use count_proc_macro::many_greetings;
fn main() {
many_greetings!(3);
}
Cargo.toml
[package]
name = "count-proc-macro"
version = "0.1.0"
authors = ["me"]
edition = "2018"
[lib]
proc-macro = true
[dependencies]
quote = "0.6"
src/lib.rs
extern crate proc_macro;
use std::iter;
use proc_macro::{Span, TokenStream, TokenTree};
use quote::{quote, quote_spanned};
/// Expands into multiple `println!("Hello");` statements. E.g.
/// `many_greetings!(3);` will expand into three `println`s.
#[proc_macro]
pub fn many_greetings(input: TokenStream) -> TokenStream {
let tokens = input.into_iter().collect::<Vec<_>>();
// Make sure at least one token is provided.
if tokens.is_empty() {
return err(Span::call_site(), "expected integer, found no input");
}
// Make sure we don't have too many tokens.
if tokens.len() > 1 {
return err(tokens[1].span(), "unexpected second token");
}
// Get the number from our token.
let count = match &tokens[0] {
TokenTree::Literal(lit) => {
// Unfortunately, `Literal` doesn't have nice methods right now, so
// the easiest way for us to get an integer out of it is to convert
// it into string and parse it again.
if let Ok(count) = lit.to_string().parse::<usize>() {
count
} else {
let msg = format!("expected unsigned integer, found `{}`", lit);
return err(lit.span(), msg);
}
}
other => {
let msg = format!("expected integer literal, found `{}`", other);
return err(other.span(), msg);
}
};
// Return multiple `println` statements.
iter::repeat(quote! { println!("Hello"); })
.map(TokenStream::from)
.take(count)
.collect()
}
/// Report an error with the given `span` and message.
fn err(span: Span, msg: impl Into<String>) -> TokenStream {
let msg = msg.into();
quote_spanned!(span.into()=> {
compile_error!(#msg);
}).into()
}
Running cargo run --example main prints three "Hello"s.
For those looking for a way to do this, there is also the seq_macro crate.
It is fairly easy to use and works out of the box with stable Rust.
use seq_macro::seq;
macro_rules! many_greetings {
($times:literal) => {
seq!{ N in 0..$times {
println!("Hello");
}}
};
}
fn main() {
many_greetings!(3);
many_greetings!(12);
}
As far as I know, no. The macro language is based on pattern matching and variable substitution, and only evaluates macros.
Now, you can implement counting with evaluation: it just is boring... see the playpen
macro_rules! many_greetings {
(3) => {{
println!("Hello");
many_greetings!(2);
}};
(2) => {{
println!("Hello");
many_greetings!(1);
}};
(1) => {{
println!("Hello");
many_greetings!(0);
}};
(0) => ();
}
Based on this, I am pretty sure one could invent a set of macro to "count" and invoke various operations at each step (with the count).

How to get index of macro repetition single element

I need to get index of macro repetition element to write next code:
struct A {
data: [i32; 3]
}
macro_rules! tst {
( $( $n:ident ),* ) => {
impl A {
$(
fn $n(self) -> i32 {
self.data[?] // here I need the index
}
),*
}
}
}
I know one way to do it: just tell user to write index by hands:
( $( $i:ident => $n:ident ),* )
But is there a more elegant way which does not require user's action?
The easiest way is to use recursion, like so:
struct A {
data: [i32; 3]
}
macro_rules! tst {
(#step $_idx:expr,) => {};
(#step $idx:expr, $head:ident, $($tail:ident,)*) => {
impl A {
fn $head(&self) -> i32 {
self.data[$idx]
}
}
tst!(#step $idx + 1usize, $($tail,)*);
};
($($n:ident),*) => {
tst!(#step 0usize, $($n,)*);
}
}
tst!(one, two, three);
fn main() {
let a = A { data: [10, 20, 30] };
println!("{:?}", (a.one(), a.two(), a.three()));
}
Note that I changed the method to take &self instead of self, since it made writing the example in the main function easier. :)
Each step in the recursion just adds 1 to the index. It is a good idea to use "typed" integer literals to avoid compilation slowdown due to lots and lots of integer inference.

Rust match fails because of unreachable pattern [duplicate]

This question already has an answer here:
Why is this match pattern unreachable when using non-literal patterns?
(1 answer)
Closed 5 years ago.
I am doing some simple stuff with rust... just touching some ground you know.
So I was playing with command line arguments, and I can't go past this:
use std::os::args;
fn main(){
let arg1 = args().get(1).to_str();
let help_command = "help";
if args().len() == 1 {
println!("No arguments.");
}
else if args().len() == 2 {
match arg1 {
help_command => println!("Do ..."),
_ => println!("no valid argument")
}
}
}
I can't compile... The error is:
main.rs:17:4: 17:5 error: unreachable pattern
main.rs:17 _ => println!("no valid argument")
^
error: aborting due to previous error
Also, I am using Rust 0.11.0-pre-nightly.
EDIT: Also, if I go with this approach:
match arg1 {
"help" => { /* ... / },
_ => { / ... */ },
}
It throws another error:
error: mismatched types: expected collections::string::String but found &'static str (expected struct collections::string::String but found &-ptr)
You can't use variables on Rust's match patterns. The code is being interpreted as binding whatever value is on arg1 as a new variable called help_command, and thus the catch-all pattern never matches.
You can use the literal string to match arg1:
match arg1 {
"help" => { /* ... */ },
_ => { /* ... */ },
}
Or use a guard:
match arg1 {
command if command == help_command => { /* ... */ },
_ => { /* ... */ }
}
If you are concerned about the type safety and/or repetition with using strings directly, you can parse the command into an enum:
enum Command {
HelpCommand,
DoStuffCommand
}
fn to_command(arg: &str) -> Option<Command> {
match arg {
"help" => Some(HelpCommand),
"do-stuff" => Some(DoStuffCommand),
_ => None,
}
}
Working example
Update (thanks #ChrisMorgan): It is also possible to use a static variable:
static HELP: &'static str = "help";
match arg1 {
HELP => { /* ... */ },
_ => { /* ... */ },
}
About the error reported in the question edit: Rust has two kinds of strings: &str (string slice) and String (owned string). The main difference is that the second is growable and can be moved. Refer to the links to understand the distinction better.
The error you are encountering is due to the fact that string literals ("foo") are of type &str, while std::os::args() is a Vec of String. The solution is simple: Use the .as_slice() method on the String to take slice out of it, and be able to compare it to the literal.
In code:
match arg1.as_slice() {
"help" => { /* ... */ },
_ => { /* ... */ },
}

How would I create a constant that could be one of several strings depending on conditions?

I want to have a constant using let that may be one of several values.
For instance:
if condition1 {
constant = "hi"
}
else if condition2 {
constant = "hello"
}
else if condition3 {
constant = "hey"
}
else if condition4 {
constant = "greetings"
}
I'm not sure how to do this with Swift and the let feature. But I'm inclined to believe it's possible, as this is in the Swift book:
Use let to make a constant and var to make a variable. The value of a constant doesn’t need to be known at compile time, but you must assign it a value exactly once.
How would I accomplish this?
As pointed out in the other answers you can't directly do this. But if you're looking to just variably set the initial value of a constant, then yes, that is possible. Here's an example with a computed property.
class MyClass {
let aConstant: String = {
if something == true {
return "something"
} else {
return "something else"
}
}()
}
I think you are looking for variable which will be assigned later inside switch-case:
let constant :String
switch conditions {
case condition1:
constant = "hi"
case condition2:
constant = "hello"
case condition3:
constant = "hey"
case condition4:
constant = "greetings"
default:
constant = "salute"
}
One option would be something like this, using a closure:
let constant: String = ({ value in
if conditionOne {
return "Hi"
} else if conditionTwo {
return "Bye"
}
return "Oops!"
})(myData /*needed for condition*/)
Or, for another twist, using generics:
func fancySwitch<S, T>(val: S, fn: S -> T) -> T {
return fn(val)
}
let x: String = fancySwitch(3) { val in
if val == 2 {
return "Hi"
} else if val < 5 {
return "Bye"
}
return "Oops"
}
let y: String = fancySwitch((3, 4)) { (a, b) in
if a == 2 {
return "Hi"
} else if b < 5 {
return "Bye"
}
return "Oops"
}
I understand what you're looking for. In Scala and some other functional languages this can be done using the match statement (kind of like switch) because the entire statement resolves to a value like this:
val b = true
val num = b match {
case true => 1
case false => 0
}
This is unfortunately not directly possible in Swift because there is no way to get a value from a branch statement. As stated in the Swift book, "Swift has two branch statements: an if statement and a switch statement." Neither of these statements resolve to a value.
The closest code structure I can think of is to first use a variable to retrieve the correct value and then assign it to a constant to be used in any later code:
let b = true
var num_mutable: Int
switch b {
case true:
num_mutable = 1
default:
num_mutable = 0
}
let num = num_mutable
Just add the line let constant: String before your if/else statement.
Below, an excerpt from Swift 1.2 and Xcode 6.3 beta - Swift Blog - Apple Developer elaborates.
let constants are now more powerful and consistent — The new rule is
that a let constant must be initialized before use (like a var), and
that it may only be initialized, not reassigned or mutated after
initialization. This enables patterns like:
let x : SomeThing
if condition {
x = foo()
} else {
x = bar()
}
use(x)
This formerly required the use of a var even though there is no
mutation taking place. Properties have been folded into this model to
simplify their semantics in initializers as well.
I found the Swift blog post above from the article "Let It Go: Late Initialization of Let in Swift", which I found by googling: swift let constant conditional initialize.