英文:
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()
,首先将其转换为OsStr
(impl AsRef<OsStr> for str
),然后将其转换为Path
(impl 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);
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论