Scenario: I've defined a description and a version constant:
const DESCRIPTION: &'static str = "my program";
const VERSION: &'static str = env!("CARGO_PKG_VERSION");
Now I want to define a version string which I'll print when the -v argument is passed:
const VERSION_STRING: &'static str = concat!(DESCRIPTION, " v", VERSION);
This currently does not work, which is not intuitive. I'd expect static constants to work just like string literals. If you want to concatenate string constants to form another string constant, you currently have to define a macro for each constant:
macro_rules! description {
() => ( "my program" )
}
macro_rules! version {
() => ( env!("CARGO_PKG_VERSION") )
}
macro_rules! version_string {
() => ( concat!(description!(), " v", version!()) )
}
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.
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.
actually that would be pretty cool.. something like "intrinsic constant functions"
This is a fundamental limitation of macros
OK, I already suspected that.
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.
So something like constexpr functions in C++? Sounds like a good idea.
@oli-obk intrinsic const fns could actually happen. There's a few good candidates, size_of
being one. Some sort of intrinsic string-concatenation function should be possible, though a bit trickier since you do actually have to store the data somewhere in order to return a reference to it.
Closing as "not a bug".
@steveklabnik Is there any other way to track the status of this feature request?
@CodeMonkey90 Feature requests usually belong in the rfcs repo.
It's been a while - is there any such feature request available in the RFCs repo? If there is, it'd be nice to reference it from this issue.
@oli-obk since const evaluation has come a long way, would it be possible use that to combine string constants into a new one? Or would that require const fn std::ops::Add
or similar?
Also, generally const fns need to also be usable at runtime with normal runtime values. With constants it isn't too hard with some compile black magic but how would a fn(&str, &str) -> &str
work at runtime? Would it just construct a String
and then leak it?
I would not want to see such a function. I'd rather make the various methods on String
const fn and use push
and push_str
. This requires heap allocations at compile time and indeed a way to leak the final value. This most definitely needs an RFC though. The implementation of the feature is rather trivial, implementing the restrictions to make it sane requires some thought.
The "best" (or at least working) solution I've seen so far is through usage of quote!
macro in procedural macro crates.
FWIW you can just work around this by having a "constant" macro as such:
macro_rules! A {() => {"a"}}
const A: &'static str = A!();
macro_rules! B {() => {"b"}}
const B: &'static str = B!();
const AB: &'static str = concat!(A!(), B!());
Why was this issue closed? There are indeed workarounds but they are just looking ugly.
In the ideal world, the below should work
const MY_URL: &str = "A";
const MY_RE: &str = "^" + MY_URL + "$";
Now my code has a mix of constants and macro calls.
@sergeken Because concat!
cannot be changed to do this. However a const fn
way of concatenating strings is possible. https://github.com/rust-lang/const-eval/issues/20 is probably the best place to follow for further developments.
const fn and others could be a way. However, these seems to be used to have runtime generated constants. My case is about compiler evaluated constant expression. My example is to combine a set of string literals at compile time. I agree it is kind of syntax sugar but high level languages are about giving stronger expressiveness. Alternatively the two consts can be written without the "+" operator but this is not following the DRY principle.
As I think about it, this issue is not only about the const keyword but adding the "+" concatenation operator to string literals. My example is actually coming from the desire to write: let re = Regex::new("^" + MY_URL + "$");
. The use of concact! is actually a workaround to my initial comment. And I hope you can see from the example the value of being able to allow such expressions.
It is possible to implement a macro to concatenate &'static str
const
ants in Rust 1.46.
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=1555757f31d8b98f514806c4f94b634c
Example use of the macro:
fn main(){
const A: &str = "hello";
const B: &str = " world";
const C: &str = concat_strs!(A, B, B, B, B);
dbg!(C);
}
prints
[src/main.rs:56] C = "hello world world world world"
The linked macro cannot concatenate constants that come from generic parameters.
It can't concatenate const parameters
fn foo<const S: &'static str>(){
concat_strs!(S, S);
}
It can't concatenate associated constants of type parameters:
fn foo<S: Foo>(){
concat_strs!(S::BAR, "");
}
This is exactly what I want. Next step is to add syntactic sugar to replace the macro with a '+' operator :-). However, I'm happy with the current syntax for declaring const's.
Is there any plan to enhance the original concat macro? Or will this be a breaking change?
The thing with making the concat
macro concatenate const
ants as well is that it would have to do two things:
&'static str
when it's given at least one const
ant.I think that might be possible? I don't really know.
Most helpful comment
It's been a while - is there any such feature request available in the RFCs repo? If there is, it'd be nice to reference it from this issue.