Why don't constants count as literals?


#1

Like the title suggests, I’m just curious as to why constants don’t count as literals.

  • This means I can’t do something like concat!("time:", syscall::CLOCK_MONOTONIC) to create the path to the redox time scheme at compile time, and instead have to either hardcode it or format!("time:{}", syscall::CLOCK_MONOTONIC).
  • It’s also annoying if I need to do some raw assembly and can’t simply
global_asm!(concat!(
    "__restore_rt:
        mov $",
    sc::nr::RT_SIGRETURN,
    "\n syscall"
));

This time, I HAVE to hardcode. I don’t think I can format! assembly.


#2

Well, because they’re not literally literals. A literal is literally a literal value, a constant is just a binding to a value, not the value itself.

But the answer you want is: because macros are processed before things like “constants” or “names” or “values” or “types” exist. The compiler literally does not know what this CLOCK_MONOTONIC thing is beyond that it’s an identifier.

So it can’t look up the value because it literally doesn’t exist yet.

… I’ll stop saying “literally” now.


#3

This is the trouble inherent to having multiple layers of compiletime abstractions. Macros live in a world that knows nothing beyond the AST (and in some places, even unparsed token streams!) and simple path resolution. They walk 5 miles through the snow to get to school—uphill, both ways!

Constants live in a world where there’s this fancy schmancy thing called a “type system.” There might even be some generics in there, and bounds to be fulfilled.


When you need reasonable abstractions in macros, the best you can hope for is that your dependencies provide constants in a macro form. Which is, of course, something completely unheard of in rust… but maybe it ought to become more common practice.

#[macro_export]
macro_rules! sc_constant {
    (nr::ACCEPT) => { 43 };
    (nr::ACCEPT4) => { 288 };
    (nr::ACCESS) => { 21 };
    ...
}

// in nr.rs
pub const ACCEPT: usize = sc_constant!{nr::ACCEPT};
pub const ACCEPT4: usize = sc_constant!{nr::ACCEPT4};
pub const ACCESS: usize = sc_constant!{nr::ACCESS};
...

If you absolutely must not hardcode something (due to being platform dependent), you could generate a file via build.rs and use it as a literal with include_str!; but you’d have to make sure the generated file uses the correct value for the target platform rather than the compiler host. (how? Don’t ask me!)