How to store colors as global constants to use them in multiple place in my code - error[E0015]: cannot call non-const fn `<&str as Colorize>::green` in constants

cargo.toml

[package]
name = "test_0"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
colored = "2.1.0"

main.rs

use colored::{ColoredString, Colorize};

fn main() {
    println!("{}", "This is red".red());
}

This example compiles without errors and it works fine.


However this will not work:

use colored::{ColoredString, Colorize};

const info_test: ColoredString = "INFO:".green().bold();

fn main() {
    println!("{} {}", info_test, "This is red".red());
}

Here i am trying to store "INFO:".green().bold() in a constant as a global variable to use it in multiple places in my code.

I am getting this error:

error[E0015]: cannot call non-const fn `<&str as Colorize>::green` in constants
 --> src/main.rs:3:42
  |
3 | const info_test: ColoredString = "INFO:".green().bold();
  |                                          ^^^^^^^
  |
  = note: calls in constants are limited to constant functions, tuple structs and tuple variants

error[E0015]: cannot call non-const fn `<ColoredString as Colorize>::bold` in constants
 --> src/main.rs:3:50
  |
3 | const info_test: ColoredString = "INFO:".green().bold();
  |                                                  ^^^^^^
  |
  = note: calls in constants are limited to constant functions, tuple structs and tuple variants

For more information about this error, try `rustc --explain E0015`.
error: could not compile `test_0` (bin "test_0") due to 2 previous errors

Is there any way to store "INFO:".green().bold() as global data so i can use it in multiple places ?

Making a ColoredString available globally is similar to making a String available globally (since ColoredString contains a String internally), either can only be created at run-time. The general approaches for creating a “global constant” of any run-time generated data thus applies:


To solve the problem of just not wanting to repeat yourself, you could define a function

use colored::{ColoredString, Colorize};

fn info_test() -> ColoredString { "INFO:".green().bold() }

fn main() {
    println!("{} {}", info_test(), "This is red".red());
}

Rust Explorer

If you also want to avoid re-computation of the ColoredString data structure, then you could use a lazily initialized static variable, e.g. with once_cell::sync::Lazy

use colored::{ColoredString, Colorize};
use once_cell::sync::Lazy;

static INFO_TEST: Lazy<ColoredString> = Lazy::new(|| "INFO:".green().bold());

fn main() {
    println!("{} {}", *INFO_TEST, "This is red".red());
}

Rust Explorer

Comparable functionality can also be achieved using the once_cell equivalent that’s already part of the standard library, though you’ll need to wrap the data, one way could be to go back to a function for that wrapping action:

use colored::{ColoredString, Colorize};
use std::sync::OnceLock;

fn info_test() -> &'static ColoredString {
    static S: OnceLock<ColoredString> = OnceLock::new();
    S.get_or_init(|| "INFO:".green().bold())
}

fn main() {
    println!("{} {}", info_test(), "This is red".red());
}

Rust Explorer

1 Like

The first solution seems to be the best one:

use colored::{ColoredString, Colorize};

fn info_test() -> ColoredString { "INFO:".green().bold() }

fn main() {
    println!("{} {}", info_test(), "This is red".red());
}

I, too, think that it’s a good straightforward solution. The main applications of lazy statics would be in values that are actually expensive to compute, or in values with interior mutability where you need to share some form of “object identity” (this was particularly prominent in the past, when Mutex::new wasn’t a const fn yet, so you couldn’t just have a static MY_LOCK: Mutex<Foo> = Mutex::new(Foo::new())).

1 Like

You could also embed the escape sequences directly into the string:

const info_test: &str = "\x1B[32mthis is green\x1B[0m";

the magic numbers can be found in the sources of the crate or in some docs on terminal escape sequences and apparently they are also on wikipedia.

1 Like

So you mean that this part "INFO:".green().bold(); is evaluated at runtime, and i am trying to store it in a const info_test: ColoredString which is evaluated at compile time ? Is it like constexpr in C++ that is evaluated only at compile time ?

Yes, const items have values that can be evaluated at compile-time. Note that const items do however not denote any global variable, so that in particular if the const is of a relatively large type (e.g. a long array), then there can still be considerable action at run-time where the const is used.

Also, the set of operations that can be done at compile-time for defining const items is so restricted that is should generally make no difference in program behavior (except for better optimization) whether you use a const item, or just inline its definition at the places you use it.

Also, unlike const NAME: Type = …; items, if we talk about const fn, those are functions that truly only say they can but not necessarily will be evaluated at compile-time. If you call a const fn in places where constants are expected, then it will be evaluated at compile-time, otherwise it will be evaluated as an ordinary function at run-time (except of course for cases of LLVM being clever and inlining and looking through all definitions to move some simple calculations to compile time after all, but that can happen for any ordinary fn, too).

Places that expect constants in Rust include

  • lengths of arrays in [value; len]-style array expressions,
  • and in the type of arrays, like [Ty; len], as well as generally when filling in const generics for types, like Foo<{expr…}> for some type struct Foo<const N: usize> for example
  • in the right-hand side of const items, or associated const items and
  • in the initializer of static variables
  • probably a few more niche cases[1]

I believe there are many similarities to constexpr in C++ here, but also I’m not sufficiently familiar with C++ in-depth to be able to point out all the differences.


  1. the reference lists enum declarations which can have constant expressions for defining custom discriminant values; I also think to remember maybe something around inline assembly syntax, where constants might be expected in some place, unless I’m confusing this with something else; also I think there’s const {} syntax for initializing thread-locals ↩︎

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.