Why is it not possible to declare a static or constant std::path::Path object in Rust?
Asked Answered
C

2

10

I am working on a CLI for learning purposes. This CLI involves a filesystem and I'd like to declare some public and static (or constant) Path objects in a "filesystem" module so that other modules in the crate can use them when needed.

I am unable to declare those in a way that satisfies my pedantry. I know it is achievable with some lazy evaluation technique but that would store the data on the heap ... given the paths are known at compile time I simply do not understand why we cannot store it as a static or constant value.

I do understand that the Path::new method should be defined with pub **const** fn new() for this to work, but given that is not the case and the compiler recommends using Lazy::new(|| ...), I imagine there is a reason even though the path is hard coded, it cannot be evaluated at compile time using static or const ...

Please note that I am not looking for workarounds that involve storing the path as a string or on the heap, but rather, a way to achieve what I want or an explanation on why it is not achievable.

Below is a snippet to better illustrate what I am trying to achieve, associated with the compiler error :

use std::path::Path;

static ROOT_DIR: &Path = Path::new("/myproject");
// static LOGS_DIR: &Path = ROOT_DIR.join("logs").as_path();


fn main() {
    println!("{}", ROOT_DIR.display());
    // println!("{}", LOGS_DIR.display());
}
error[E0015]: cannot call non-const fn `Path::new::<str>` in statics
 --> src/main.rs:4:26
  |
4 | static ROOT_DIR: &Path = Path::new("/myproject");
  |                          ^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: calls in statics are limited to constant functions, tuple structs and tuple variants
  = note: consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell

For more information about this error, try `rustc --explain E0015`.
error: could not compile `shaka` due to previous error
Changeless answered 27/5, 2023 at 19:15 Comment(0)
H
9

Unfortunately, this is not possible currently, even on nightly.

The reason is because Path::new() is defined as a generic method. Path::new(str) is just another way of saying str.as_ref().as_ref() (invoking AsRef::as_ref(), first to convert to OsStr (impl AsRef<OsStr> for str) then to convert this to Path (impl AsRef<Path> for OsStr), just more clear and better fit for inference. Path::new() accepts everything that implements AsRef<OsStr>.

To constify this, we would have to constify the AsRef implementation and constrain AsRef to be const. And this is only possible on nightly currently, with major design questions. So there is no way we cannot make Path::new() const on stable.

There is an opened PR for adding methods that can do that const-way, but it is not merged and it is not clear whether we want to commit to this workaround.

Hephzipah answered 27/5, 2023 at 19:28 Comment(0)
G
-3

If you really, really want to do this, it's possible with unsafe code.

use std::path::Path;

const ROOT_DIR: &Path = {
    let s = "/myproject";
    unsafe { std::mem::transmute(s.as_bytes()) }
};

fn main() {
    println!("{:?}", ROOT_DIR);
}
Guendolen answered 28/5, 2023 at 1:39 Comment(3)
This is unsound. The layout of Path is not guaranteed.Hephzipah
Is it not just an OsStr, which is in turn a sequence of bytes?Guendolen
Nothing of this is specified. Not that &Path is just &OsStr, and not what OsStr is.Hephzipah

© 2022 - 2024 — McMap. All rights reserved.