I have many repeated constants in the form of:
pub const FOO_REGEX: Regex = Regex::new(r"foo.*").unwrap();
pub const BAR_REGEX: Regex = Regex::new(r"bar.*").unwrap();
I'd like to simply this by using a macro_rules! macro.
I tried:
macro_rules! pattern {
($value:literal) => {
Regex::new($value).unwrap()
}
}
pub const FOO_REGEX: Regex = pattern!(r"foo.*");
But the compiler complains with:
error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants
--> lib.rs:7:9
|
7 | Regex::new($value).unwrap()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
11 | pub const FOO_REGEX: Regex = pattern!(r"foo.*");
| ------------------ in this macro invocation
Per this guide I tried many of the available designators options, like expr, ident, but I'm still not able to get macro to compile.
Why does the literal designator not work for this macro expression?
This has nothing to do with macros: you get the same error if you write the code directly (playground). The problem here is that calling Regex::new is not a literal (1) and cannot be evaluated at compile-time (yet?). You will need to use something like the lazy_static crate to ensure that Regex::new is called at run-time to compile the regular expression:
use regex::Regex;
use lazy_static::lazy_static;
lazy_static!{
pub static ref FOO_REGEX: Regex = Regex::new(r"foo.*").unwrap();
}
Playground
(1) Quoting from this answer:
A literal is a value written as-is in the code: true, 1, "hello"; the result of an expression [like Regex::new] cannot be a literal (by definition). The resulting types may look similar (or even be identical) but types are irrelevant here.
Related
Let's say I have the following Rust enum:
enum Food {
Fruit(String), // the String represents the food's name
Vegitable(String),
}
How can I test that a function actually generates an apple and not a banana?
For instance, I want a test something like the following:
#[test]
fn is_correct_food() {
let apple = Food::Fruit("Banana".to_string()); // comes from tested function
assert!(matches!(apple, Food::Fruit("Apple".to_string())));
}
Playground link
When I run the above code, I get the following error:
error: expected one of `)`, `,`, `...`, `..=`, `..`, or `|`, found `.`
--> src/lib.rs:8:48
|
8 | assert!(matches!(apple, Food::Fruit("Apple".to_string())));
| ^
| |
| expected one of `)`, `,`, `...`, `..=`, `..`, or `|`
| help: missing `,`
After doing some research, I learned this error occurs because you cannot call functions inside pattern matching statements. If I try and instead abstract the name into a variable (to remove the function call), the test passes because the pattern matches.
#[test]
fn is_correct_food() {
let apple = Food::Fruit("Banana".to_string()); // comes from tested function
let name = "Apple".to_string();
assert!(matches!(apple, Food::Fruit(name)));
}
Playground link
Undesirable solution
It is possible to use a match statement to get the variable out from the contents. However, logic should be avoided in tests at all costs. Additionally, the actual object I need to test is a lot more complicated, which would require many nested match statements. So I'd like to avoid anything that looks like the following test:
#[test]
fn is_correct_food() {
let apple = Food::Fruit("Banana".to_string()); // comes from tested function
match apple {
Food::Fruit(name) => assert_eq!(name, "apple"),
_ => panic!("should be a fruit"),
}
}
Related questions
How to match a String against string literals?
Does pattern matching with a top-level string. I want pattern matching inside a nested object.
Much like you can add an if after the pattern in a match block, called a match guard, you can do the same with matches!:
matches!(apple, Food::Fruit(fruit) if fruit == "Apple")
The documentation for matches! mentions this functionality and includes an example that demonstrates it.
So say I'm writing a wrapper type for the array.
struct Array<const L: usize, T> {
raw: [T;L]
}
And I have some function that mutates the length of the array wrapper, say the function is concatenation:
impl<const L: usize, T> Array<L, T> {
fn concat<const L2: usize>(self, other: Array<L, T>) -> Array<{L + L2}, T> {todo!();}
}
When I try to compile this code, the rust compiler gets extremely mad. Thinking it might have something to do with adding types corresponding to implementing multiple, traits, i tried multiplication instead of addition, which also didn't work.
I know that rust can evaluate some expressions at compile time, is this just a case where that isn't allowed, or am I missing something?
When I try to compile this code, the rust compiler gets extremely mad. […] I know that rust can evaluate some expressions at compile time, is this just a case where that isn't allowed, or am I missing something?
You say that the compiler gets mad at you, but have you considered listening at what it was telling you?
Plugging your code into the playground, the first error is a trivial showstopper of
error: type parameters must be declared prior to const parameters
--> src/lib.rs:1:30
|
1 | struct Array<const L: usize, T> {
| -----------------^- help: reorder the parameters: lifetimes, then types, then consts: `<T, const L: usize>
so it kind-of doesn't seem to because that's trivial to fix.
Now fixing that we get to the meat of the issue:
= help: const parameters may only be used as standalone arguments, i.e. `L`
= help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions
that seems to explain the entirety of the thing: what's currently enabled in Rust is the
Const generics MVP
MVP as in minimum viable products aka the smallest featureset which can be useful, and what that translates to is explained in the introductory blog post.
The first limitation is
Only integral types are permitted for const generics
which is fine here because you're using integral types, but the second limitation is
No complex generic expressions in const arguments
What is a complex generic expression? Anything outside of:
A standalone const parameter.
A literal (i.e. an integer, bool, or character).
A concrete constant expression (enclosed by {}), involving no generic parameters.
What you're trying to do does not work (outside of nightly with both const_generics and const_evaluatable_checked enabled) because you're writing constant expressions involving at least one generic parameter, which is not part of the MVP.
This extremely simple Rust program:
fn main() {
let c = "hello";
println!(c);
}
throws the following compile-time error:
error: expected a literal
--> src/main.rs:3:14
|
3 | println!(c);
| ^
In previous versions of Rust, the error said:
error: format argument must be a string literal.
println!(c);
^
Replacing the program with:
fn main() {
println!("Hello");
}
Works fine.
The meaning of this error isn't clear to me and a Google search hasn't really shed light on it. Why does passing c to the println! macro cause a compile time error? This seems like quite unusual behaviour.
This should work:
fn main() {
let c = "hello";
println!("{}", c);
}
The string "{}" is a template where {} will be replaced by the next argument passed to println!.
TL;DR If you don't care why and just want to fix it, see the sibling answer.
The reason that
fn main() {
let c = "hello";
println!(c);
}
Cannot work is because the println! macro looks at the string at compile time and verifies that the arguments and argument specifiers match in amount and type (this is a very good thing!). At this point in time, during macro evaluation, it's not possible to tell that c came from a literal or a function or what have you.
Here's an example of what the macro expands out to:
let c = "hello";
match (&c,) {
(__arg0,) => {
#[inline]
#[allow(dead_code)]
static __STATIC_FMTSTR: &'static [&'static str] = &[""];
::std::io::stdio::println_args(&::std::fmt::Arguments::new(
__STATIC_FMTSTR,
&[::std::fmt::argument(::std::fmt::Show::fmt, __arg0)]
))
}
};
I don't think that it's actually impossible for the compiler to figure this out, but it would probably take a lot of work with potentially little gain. Macros operate on portions of the AST and the AST only has type information. To work in this case, the AST would have to include the source of the identifier and enough information to determine it's acceptable to be used as a format string. In addition, it might interact poorly with type inference - you'd want to know the type before it's been picked yet!
The error message asks for a "string literal". What does the word "literal" mean? asks about what that means, which links to the Wikipedia entry:
a literal is a notation for representing a fixed value in source code
"foo" is a string literal, 8 is a numeric literal. let s = "foo" is a statement that assigns the value of a string literal to an identifier (variable). println!(s) is a statement that provides an identifier to the macro.
If you really want to define the first argument of println! in one place, I found a way to do it. You can use a macro:
macro_rules! hello {() => ("hello")};
println!(hello!());
Doesn't look too useful here, but I wanted to use the same formatting in a few places, and in this case the method was very helpful:
macro_rules! cell_format {() => ("{:<10}")}; // Pads with spaces on right
// to fill up 10 characters
println!(cell_format!(), "Foo");
println!(cell_format!(), 456);
The macro saved me from having to duplicate the formatting option in my code.
You could also, obviously, make the macro more fancy and take arguments if necessary to print different things with different arguments.
If your format string will be reused only a moderate number of times, and only some variable data will be changed, then a small function may be a better option than a macro:
fn pr(x: &str) {
println!("Some stuff that will always repeat, something variable: {}", x);
}
pr("I am the variable data");
Outputs
Some stuff that will always repeat, something variable: I am the variable data
This question already has answers here:
println! error: expected a literal / format argument must be a string literal
(4 answers)
Closed 6 years ago.
I have a HashMap with endpoints:
#[derive(Debug, Clone, Copy, Ord, Eq, PartialOrd, PartialEq, Hash)]
enum Endpoint {
OauthToken,
Disciplines,
PublicTournaments,
MyTournaments,
Matches,
}
lazy_static! {
static ref API_EP: HashMap<Endpoint, &'static str> = {
let mut m: HashMap<Endpoint, &'static str> = HashMap::new();
m.insert(Endpoint::OauthToken, "/oauth/v2/token");
m.insert(Endpoint::Disciplines, "/v1/disciplines");
m.insert(Endpoint::PublicTournaments, "/v1/tournaments");
m.insert(Endpoint::MyTournaments, "/v1/me/tournaments");
m.insert(Endpoint::Matches, "/v1/tournaments/{}/matches");
m
};
}
As you may see, the map item which is accessible by Endpoint::Matches key returns a string which needs to be formatted, however, I don't see a way of doing this.
I've tried to use format! macro but it needs string literals but not an object so it does not work. I tried to search the solution over the internet and rust std library but I was unable to find it. Is there any way to do this?
For clarification why format! does not work:
error: format argument must be a string literal.
--> src/lib.rs:249:31
|
249 | let address = format!(get_ep_address(Endpoint::Matches)?, id.0);
get_ep_address(Endpoint::Matches)? is not a string literal so it can't work.
I would also like to know how to format both &str and String types.
The format! macro expects a literal because it invokes compiler magic (format_args!) to check the format at compile and ensure that its arguments implement the necessary trait. For example {} requires that the argument implements Display, as described in Formatting traits.
You might be able to cobble something together by reaching to Formatter directly, but it means parsing the str yourself.
Note: Rust does not feature reflection, so checking whether a type implements a given trait at runtime is not easy...
I am trying to instantiate an argument parser (clap).
There is code like:
const DEFAULT_VALUE: &'static str = "12312312";
// ...
.help("this parameter is for (default:" + DEFAULT_VALUE + ")")
// ...
I looked through similar existing questions and discovered concat! macro and lazy_static.
First option doesn't fit and there is no example for lazy_static.
If it's possible it's will be over complicated anyway because lazy_static requires to define a block in a separate place.
I am looking some concise syntax sugar with a macro in place, without a lot of type overhead.
If define a local variable it could get to high because DSL for clap could be very long. It's not convenient, because it rips the string from its logical place in code.
Another approach to define static variable for the whole help string but it posses the same drawback as the approach above plus namespace pollution.
Suggested solution with format! doesn't fit too. It requires to define a local variable.
Example
extern crate clap;
use clap::{Arg, App};
const DEFAULT: &'static str = "1";
fn main() {
let params = App::new("app")
.arg(Arg::with_name("p")
// here 100 lines of the uninterruptable expression
.help(&format!("parameter p (DEFAULT: {})", DEFAULT)))
// here also 100 lines of the uninterruptable expression
.get_matches();
println!("param p = {}", params.value_of("p").unwrap())
}
Cargo.toml
[package]
name = "demo-clap"
version = "1.0.0"
[dependencies]
clap = "2.10.0"
Compilation error
<std macros>:2:1: 2:61 error: borrowed value does not live long enough
<std macros>:2 $ crate :: fmt :: format ( format_args ! ( $ ( $ arg ) * ) ) )
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/main.rs:11:21: 11:66 note: in this expansion of format! (defined in <std macros>)
src/main.rs:13:24: 15:2 note: reference must be valid for the block suffix following statement 0 at 13:23...
src/main.rs:13 .get_matches();
^
src/main.rs:8:5: 13:24 note: ...but borrowed value is only valid for the statement at 8:4
src/main.rs:8 let params = App::new("app")
^
src/main.rs:8:5: 13:24 help: consider using a `let` binding to increase its lifetime
src/main.rs:8 let params = App::new("app")
^
error: aborting due to previous error
error: Could not compile `demo-clap`.
If you're working with Rust 1.46.01 or later, check out the const_format crate on (crates.io | docs.rs).
concatcp: Concatenates integers2, bool, and &str constants into &'static str.
formatcp: format-like formatting (emits a &'static str); takes the same primitives as concatcp
So for your example, formatcp would provide the most flexible solution and does not require the local variable you mention (I'm assuming you're referring to the heap-allocated String resulting from alloc::fmt::format in the format! macro):
use clap::{Arg, App};
use const_format::formatcp;
const DEFAULT: &'static str = "1";
fn main() {
let params = App::new("app")
.arg(Arg::with_name("p")
.help(formatcp!("parameter p (DEFAULT: {})", DEFAULT)))
.get_matches();
println!("param p = {}", params.value_of("p").unwrap())
}
Running with app -h gives
app
USAGE:
app [p]
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
ARGS:
<p> parameter p (DEFAULT: 1)
Limitations of all macros in the crate:
Macros that expand to &'static str (i.e. the ones I mentioned) only accept constants from concrete types:
Type::<u8>::FOO OK ✅
Type::<TYPE_PARAMETER>::FOO BAD ❌
Integer arguments need to be constrained (i.e. must add i*/u* suffix when using literals).
Can't be used in some places that take string literals only, namely attributes which can require LiteralExpression.
#[doc = "ab"] != #[doc = concatcp!("a", "b")]
1 This is needed for the stabilized const fn improvements, which allow for looping without "heinous hackery" involving std::mem::transmute
2 More specifically, this would be all the i*/u* primitives. Note that if you'll have to constrain the type yourself by using the suffix desired if you're passing in literals.
You can simply use a reference and the format! macro:
.help(&format!("this parameter is for (default: {})", DEFAULT_VALUE));
Edit:
What you want to do is not possible in Rust:
This is a fundamental limitation of macros in that they are working
with nothing more than various tokens. They have no understanding of
types, just tokens that look like types. When concat! sees DESCRIPTION
it just sees an identifier, it has no idea that it is a string
constant. What could work here though is some sort of string
concatenation const fn as that could take the values of constants to
create new constants, although that would require some dark magic.
You could do this instead:
macro_rules! DEFAULT {
() => { "1" };
}
fn main() {
let params = App::new("app")
.arg(Arg::with_name("p")
// here 100 lines of the uninterruptable expression
.help(concat!("parameter p (DEFAULT: ", DEFAULT!(), ")")))
// here also 100 lines of the uninterruptable expression
.get_matches();
println!("param p = {}", params.value_of("p").unwrap())
}
The use of a macro instead of a constant allows you to use the concat! macro.