'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 constants or statics 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 Foos.

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