编写包含字符串并可在常量中使用的 Rust 结构类型

Kev*_*eid 11 rust

我正在开始使用 Rust。我想要一个包含(除其他外)字符串的结构:

\n
#[derive(Clone, Debug)]\nstruct Foo {\n    string_field: &str,  // won't compile, but suppose String or Box<str> or &'a str or &'static str...\n}\n
Run Code Online (Sandbox Code Playgroud)\n

我希望能够声明它的constants 或s:static

\n
static FOO1: Foo = Foo {\n    string_field: "",\n};\n
Run Code Online (Sandbox Code Playgroud)\n

希望能够让它包含在运行时构造的字符串:

\n
let foo2 = Foo {\n  string_field: ("a".to_owned() + "b").as_str(),\n};\n
Run Code Online (Sandbox Code Playgroud)\n

我可以添加一个生命周期参数,Foo以便我可以声明字符串引用具有相同的生命周期。这很好,只是它似乎需要为包含a的所有内容提供显式生命周期参数Foo,这意味着它使我的程序的其余部分变得复杂(甚至是不关心是否能够使用常量表达式的部分)。

\n

我可以写

\n
enum StringOfAdequateLifetime {\n    Static(&'static str),\n    Dynamic(Box<str>),  // or String, if you like\n}\nstruct Foo {\n    string_field: StringOfAdequateLifetime,\n}\n
Run Code Online (Sandbox Code Playgroud)\n

到目前为止,这似乎有效,但写出文字Foos 却变得混乱。

\n

显然,所需的运行时行为是合理的:当您删除 a 时Foo,删除它包含的字符串 \xe2\x80\x94 ,如果它是静态的,则永远不会删除,因此不需要额外的信息来处理这两种情况。有没有一种干净的方法来向 Rust 请求这个?

\n

(似乎我可以使用某种“智能指针”类型来保存字符串,该字符串也可以写为静态情况下的常量表达式,但我还没有在标准库中看到它,当我试图泛化StringOfAdequateLifetime以应用于任何类型,我在实现和使用各种标准特征(例如 )时遇到了进一步的复杂性,我怀疑这是由于类型和非类型Deref之间的差异所致。)SizedSized

\n

ape*_*lla 10

Rust 标准库有一个针对这个具体用例的内置类型Cow。它是一个枚举,可以表示引用或拥有的值,并在必要时克隆该值以允许可变访问。在您的特定用例中,您可以像这样定义结构:

struct Foo {
    string_field: Cow<'static, str>
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以通过两种方式之一实例化它,具体取决于您是想要借用的常量字符串还是拥有的运行时构造的值:

const BORROWED: Foo = Foo { string_field: Cow::Borrowed("some constant") };
let owned = Foo { string_field: Cow::Owned(String::from("owned string")) };
Run Code Online (Sandbox Code Playgroud)

为了简化此语法,您可以使用 fn 为该类型定义自己的构造函数const,以允许在常量上下文中使用借用的构造函数:

impl Foo {
    pub const fn new_const(value: &'static str) -> Self {
        Self { string_field: Cow::borrowed(value) }
    }

    pub fn new_runtime(value: String) -> Self {
        Self { string_field: Cow::Owned(value) }
    }
}
Run Code Online (Sandbox Code Playgroud)

这允许您使用更简单的语法来初始化值:

const BORROWED: Foo = Foo::new_const("some constant");
let owned = Foo::new_runtime(String::from("owned string"));
Run Code Online (Sandbox Code Playgroud)