为什么在Rust中无法声明静态或常量std::path::Path对象?

huangapple go评论81阅读模式
英文:

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

问题

我正在为学习目的开发一个命令行界面(CLI)。这个CLI涉及到文件系统,我想在一个名为"filesystem"的模块中声明一些公共的静态(或常量)Path对象,以便在需要时其他模块可以使用它们。

我无法以满足我的苛刻要求的方式声明它们。我知道可以通过一些惰性评估技巧来实现,但那会将数据存储在堆上... 鉴于这些路径在编译时已知,我简单地不明白为什么我们不能将其存储为静态或常量值。

我理解Path::new方法应该以pub **const** fn new()的方式定义才能使其工作,但鉴于情况并非如此,而且编译器建议使用Lazy::new(|| ...),我想象即使路径是硬编码的,也有一个原因,不能在编译时使用静态或常量来评估它...

请注意,我不是在寻找涉及将路径存储为字符串或堆上的解决方法,而是要找到实现我想要的方式,或者解释为什么不能实现。

以下是一个代码片段,更好地说明了我试图实现的目标,以及与编译器错误相关的内容:

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]: 无法在静态变量中调用非常量的fn `Path::new::<str>`
 --> src/main.rs:4:26
  |
4 | static ROOT_DIR: &Path = Path::new("/myproject");
  |                          ^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: 静态变量中的调用仅限于常量函数、元组结构体和元组变体
  = note: 考虑使用`once_cell`库中的`Lazy::new(|| ...)`将此表达式包装起来: https://crates.io/crates/once_cell

有关此错误的更多信息,请尝试运行`rustc --explain E0015`。
error: 由于先前的错误,无法编译`shaka`
英文:

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

答案1

得分: 5

抱歉,目前这是不可能的,即使在nightly版本也不行。

原因是因为Path::new()被定义为一个泛型方法。Path::new(str)只是另一种方式来表示str.as_ref().as_ref()(调用AsRef::as_ref(),首先将其转换为OsStrimpl AsRef<OsStr> for str),然后将其转换为Pathimpl AsRef<Path> for OsStr),只是更清晰,更适合推断。Path::new()接受任何实现AsRef<OsStr>的东西。

要使其成为常量,我们必须将AsRef实现为常量,并将AsRef限定为const目前这只在nightly版本上可能,还涉及重要的设计问题。因此,在稳定版本上我们无法将Path::new()设为常量。

已经有一个待处理的PR来添加可以以这种常量方式执行的方法,但它尚未合并,也不清楚我们是否要采用这种变通方法。

英文:

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.

答案2

得分: -1

如果你真的非常想要这样做,可以使用unsafe代码。

use std::path::Path;

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

fn main() {
    println!("{:?}", ROOT_DIR);
}
英文:

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);
}

huangapple
  • 本文由 发表于 2023年5月28日 03:15:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/76348617.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定