How to concatenate a literal string with a const string? - rust

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.

Related

Read file to string, return new strings rather than references?

I use rust to open a file and parse its contents.
let file = fs::read_to_string(file_path).unwrap();
I then iterate over each line:
for item in file.lines() {
// processing
}
at this point item is of type &str. I now need only a slice of that so I:
let string_slice = &item[0..2];
however, string_slice is now of type &&str (two &s). I want to return a vec of strings, however the file I read doesnt need to live longer than the function scope I open it in. I can get to a string by calling to_string() twice; but this seems awkward.
let string_slice = &item[0..2].to_string();
return string_slice.to_string() //have to call twice
is there a more elegant way to pass ownership of these slices to a vec that I then return from the function?
The other answer explains what happens under the hood, but if you want to know the idiomatic way to achieve what you're trying to, this is it:
fn get_lines() -> Vec<String> {
// Replace this with your actual input. You can `expect()` the value
// (instead of `unwrap()`) if you're sure it's there or return a `Result`
// if you're not.
// You can also pass it in as an argument. In that case, the parameter type
// should be `&str` instead of `String`. This is a common mistake among
// beginners.
let sample_input = String::from("one\ntwo\nthree\n");
sample_input
.lines()
// You can use `to_string()`, `to_owned()`, or `into()` to convert a
// string slice into a string. Either works. I find the automatic type
// inference of `into()` most satisfying, but it's up to preference in
// this case.
// The type annotation for `line` is optional.
.map(|line: &str| line[0..2].into())
// Because the function returns a `Vec`, you can omit the type. I.e.,
// you don't have to write `collect::<Vec<_>>()`. It's inferred.
.collect()
}
Run this snippet on Rust Playground.
let string_slice: &str = &item[0..2];
however, string_slice is now of type &&str (two &s).
That's not right, the type of item[0..2] is str so you have to take a reference to use it since it's unsized,
the type of &item[0..2] is then the &str you can work with.
When you directly invoke a method on it like here:
let string_slice: &String = &item[0..2].to_string();
due to auto (de-)referencing the compiler will add an automatic reference to the string slice so it desugars to this:
let string_slice: &String = &( // <- the referen you take explicitly
/*the reference the compiler inserts -> */ (&item[0..2]).to_string()
);
I added an extra pair of parentheses to make the precedence clear. So after this string_slice which is a bit of a misnomer here is of type &String.
To get a String instead you can just remove your manual reference.
let string_slice: String = item[0..2].to_string();
which desugars to
let string_slice: String = (&item[0..2]).to_string();
I've annotated all the types above for ease of understanding, but they're exactly what the compiler would infer for them.
As a tip to get the compiler to tell you what type a variable is if you don't have editor support which can tell you you can always add a type annotation of : () and the compiler will error with a message telling you what it expected the type to be:
let string_slice: () = &item[0..2];
will error with something like the following
error[E0308]: mismatched types
--> src/main.rs:2:17
|
| let string_slice: () = &item[..];
| -- ^^^^^^^^^ expected `()`, found `&str`
| |
| expected due to this
For more information about this error, try `rustc --explain E0308`.

Why the result of `Regex::new` cannot be assigned to a constant?

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.

Formatting a const string in Rust [duplicate]

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

How to provide a type-annotation for `AsRef`?

The following code shows were the question originates. Since as_slice() is deprecated, as_ref() is suggested as replacement.
However, when using it in this context, a type annotation is required.
let s = "Hi"; // This is a string slice
// warning: use of deprecated item: use std::convert::AsRef<str> instead, #[warn(deprecated)] on by default
assert!(s.replace("Hi", "Ho").as_slice() == "Ho");
// tests/lang.rs:120:35: 120:43 error: type annotations required: cannot resolve `collections::string::String : core::convert::AsRef<_>` [E0283]
// assert!(s.replace("Hi", "Ho").as_ref() == "Ho");
How can I provide such a type-annotation ?. The only syntax I would find somewhat applicable is <MyType as AsRef>::as_ref(), but I don't know how to do that with an instance.
I am using rustc 1.0.0-nightly (be9bd7c93 2015-04-05) (built 2015-04-05).
In your precise case of String and &str, the simplest is to use the Index syntax:
let s: String = "foo".to_string();
let slice: &str = &s[..]; // full range index
which in your case, would give:
let s = "Hi";
assert!(&s.replace("Hi", "Ho")[..] == "Ho");
However, for traits methods like as_ref(), you can also call them using the syntax:
Trait::method(obj); // equivalent to obj.method();
Which allow you to set the types parameters like this:
Trait::<T>::method(obj);
In your case, an alternate syntax would thus be:
let s = "Hi";
assert!(AsRef::<str>::as_ref(&s.replace("Hi", "Ho")) == "Ho");
In this particular case, you don’t need any fanciness at all: String is comparable with &str (there is a PartialEq<&str> implementation on String), so you can just compare them directly:
let s = "Hi";
assert!(s.replace("Hi", "Ho") == "Ho");
// Or, if you prefer:
assert_eq!(s.replace("Hi", "Ho"), "Ho");
In real life you will not often need to call as_ref for reasons like this.

Reversing a string in Rust

What is wrong with this:
fn main() {
let word: &str = "lowks";
assert_eq!(word.chars().rev(), "skwol");
}
I get an error like this:
error[E0369]: binary operation `==` cannot be applied to type `std::iter::Rev<std::str::Chars<'_>>`
--> src/main.rs:4:5
|
4 | assert_eq!(word.chars().rev(), "skwol");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: an implementation of `std::cmp::PartialEq` might be missing for `std::iter::Rev<std::str::Chars<'_>>`
= note: this error originates in a macro outside of the current crate
What is the correct way to do this?
Since, as #DK. suggested, .graphemes() isn't available on &str in stable, you might as well just do what #huon suggested in the comments:
fn main() {
let foo = "palimpsest";
println!("{}", foo.chars().rev().collect::<String>());
}
The first, and most fundamental, problem is that this isn't how you reverse a Unicode string. You are reversing the order of the code points, where you want to reverse the order of graphemes. There may be other issues with this that I'm not aware of. Text is hard.
The second issue is pointed out by the compiler: you are trying to compare a string literal to a char iterator. chars and rev don't produce new strings, they produce lazy sequences, as with iterators in general. The following works:
/*!
Add the following to your `Cargo.toml`:
```cargo
[dependencies]
unicode-segmentation = "0.1.2"
```
*/
extern crate unicode_segmentation;
use unicode_segmentation::UnicodeSegmentation;
fn main() {
let word: &str = "loẅks";
let drow: String = word
// Split the string into an Iterator of &strs, where each element is an
// extended grapheme cluster.
.graphemes(true)
// Reverse the order of the grapheme iterator.
.rev()
// Collect all the chars into a new owned String.
.collect();
assert_eq!(drow, "skẅol");
// Print it out to be sure.
println!("drow = `{}`", drow);
}
Note that graphemes used to be in the standard library as an unstable method, so the above will break with sufficiently old versions of Rust. In that case, you need to use UnicodeSegmentation::graphemes(s, true) instead.
If you are just dealing with ASCII characters, you can make the reversal in place with the unstable reverse function for slices.
It is doing something like that:
fn main() {
let mut slice = *b"lowks";
let end = slice.len() - 1;
for i in 0..end / 2 {
slice.swap(i, end - i);
}
assert_eq!(std::str::from_utf8(&slice).unwrap(), "skwol");
}
Playground

Resources