'Writing a Rust struct type that contains a string and can be used in a constant
I'm getting started with Rust. I want to have a struct that contains (among other things) a string:
#[derive(Clone, Debug)]
struct Foo {
string_field: &str, // won't compile, but suppose String or Box<str> or &'a str or &'static str...
}
And I want to be able to declare const
ants or static
s of it:
static FOO1: Foo = Foo {
string_field: "",
};
And I also want to be able to have it contain a string constructed at runtime:
let foo2 = Foo {
string_field: ("a".to_owned() + "b").as_str(),
};
I could add a lifetime parameter to Foo
so that I can declare that the string reference has the same lifetime. That's fine, except that it then seems to require an explicit lifetime parameter for everything that contains a Foo
, which means that it complicates the rest of my program (even parts that don't care about being able to use constant expressions).
I could write
enum StringOfAdequateLifetime {
Static(&'static str),
Dynamic(Box<str>), // or String, if you like
}
struct Foo {
string_field: StringOfAdequateLifetime,
}
and that seems to work so far but clutters up writing out literal Foo
s.
It seems obvious enough that the desired runtime behavior is sound: when you drop a Foo
, drop the string it contains — and if it's static it's never dropped, so no extra information is needed to handle the two cases. Is there a clean way to ask Rust for just that?
(It seems like what I could use is some kind of "smart pointer" type to hold the string that can also be written as a constant expression for the static case, but I haven't seen one in the standard library, and when I tried to genericize StringOfAdequateLifetime
to apply to any type, I ran into further complications with implementing and using the various standard traits like Deref
, which I suspect were due to something about the differences between Sized
and non-Sized
types.)
Solution 1:[1]
The rust standard library has a built-in type for this exact use case, Cow. It's an enum that can represent either a reference or an owned value, and will clone the value if necessary to allow mutable access. In your particular use case, you could define the struct like so:
struct Foo {
string_field: Cow<'static, str>
}
Then you could instantiate it in one of two ways, depending on whether you want a borrowed constant string or an owned runtime-constructed value:
const BORROWED: Foo = Foo { string_field: Cow::Borrowed("some constant") };
let owned = Foo { string_field: Cow::Owned(String::from("owned string")) };
To simplify this syntax, you can define your own constructor functions for the type using a const
fn to allow using the borrowed constructor in a constant context:
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) }
}
}
This allows you to use a simpler syntax for initializing the values:
const BORROWED: Foo = Foo::new_const("some constant");
let owned = Foo::new_runtime(String::from("owned string"));
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |