In Rust, it's possible to assert that the result of an expression is a compile-time constant by using it to initialise a const
. However, this sort of check doesn't work across a function boundary. For example, the following code errors at compile time:
struct StringLiteral(&'static str);
impl StringLiteral {
const fn new(s: &'static str) -> Self {
const _: &'static str = s;
StringLiteral(s)
}
}
with "attempt to use a non-constant value in a constant", even if StringLiteral::new
is never called.
It seems like it should be possible to make this sort of assertion by checking to see whether the new
function is actually running at compile-time or not – const fn
s can be called either at runtime or compile-time, and somehow asserting that the function is being run at compile-time would be one possible solution to the problem. But I haven't been able to find a function/method/intrinsic in the standard library that would determine that.
Is it possible to write a const fn
such that it runs successfully only when its argument is a compile-time constant (either via detecting whether it's being run at compile-time, or via some other mechanism)?
One approach that does not work: simply requiring a 'static
lifetime is insufficient because it's possible to create values with such lifetimes during runtime, e.g. via the use of String::into_boxed_str
followed by Box::leak
.
(Context: I'm attempting to produce a data type that can contain only literal strings, not strings that were calculated at runtime, as a sort of defence-in-depth mechanism that statically guarantees that the strings can't be controlled by an attacker: the requirement that a string must have been literally present in the program's source code makes it impossible for an attacker to put their own custom strings there.)
From Sven's comment, you can create a trait with an associated constant.
pub trait CompileTime {
const C: &'static str;
}
pub struct HelloStr;
impl CompileTime for HelloStr {
const C: &'static str = "Hello";
}
This is a decent amount of boilerplate for a simple thing, so you may want a macro, such as:
macro_rules! comptime {
($name:ident, $string:literal) => {
pub struct $name;
impl CompileTime for $name {
const C: &'static str = $string;
}
}
}
comptime!(HelloStr, "Hello");
When adt_const_params
becomes stable, you can use a const generic &str
, which works better with your original code:
const fn new<const S: &'static str>()