124

I want the members to be owned by the struct. I'm looking for the correct declaration of a struct and instantiation examples. I wasn't able to find an example.

1
  • 1
    Use String and be aware that creating a String from &str via .to_string() involves a heap allocation + copying the whole string. Commented Sep 11, 2014 at 12:13

2 Answers 2

172

If the string has to be owned by the struct, then you should use String. Alternatively, you could use an &str with a static lifetime (i.e., the lifetime of the program). For example:

struct Foo {
    bar: String,
    baz: &'static str,
}

fn main() {
    let foo = Foo {
        bar: "bar".to_string(),
        baz: "baz",
    };
    println!("{}, {}", foo.bar, foo.baz);
}

If the lifetime of the string is unknown, then you can parameterize Foo with a lifetime:

struct Foo<'a> {
    baz: &'a str,
}

See also:

If you're not sure whether the string will be owned or not (useful for avoiding allocations), then you can use borrow::Cow:

use std::borrow::Cow;

struct Foo<'a> {
    baz: Cow<'a, str>,
}

fn main() {
    let foo1 = Foo {
        baz: Cow::Borrowed("baz"),
    };
    let foo2 = Foo {
        baz: Cow::Owned("baz".to_string()),
    };
    println!("{}, {}", foo1.baz, foo2.baz);
}

Note that the Cow type is parameterized over a lifetime. The lifetime refers to the lifetime of the borrowed string (i.e., when it is a Borrowed). If you have a Cow, then you can use borrow and get a &'a str, with which you can do normal string operations without worrying about whether to allocate a new string or not. Typically, explicit calling of borrow isn't required because of deref coercions. Namely, Cow values will dereference to their borrowed form automatically, so &*val where val has type Cow<'a, str> will produce a &str.

Sign up to request clarification or add additional context in comments.

7 Comments

Uhhh, thank you for the full answer. I need to think about the lifetime parametrization another day :)
if I have a structure that includes the Copy attribute (eg #[derive(Clone, Copy)], then String doesn't compile. What is the best solution?
Don't use String? That might seem like a curt answer, but the way you asked your question doesn't leave me much choice. I suspect you are hitting am instance of the XY problem. I'd recommend asking a new question detailing the actual problem you're trying to solve.
The documentation states that String is mutable. If I have a struct with, say, a name field that should be immutable, should I still use String instead of str?
I don't see how that's relevant to this question. I'd suggest you ask a new question.
|
0

I want the members to be owned by the struct.

String is the easiest choice, though it may not be the most efficient.

Box<str> is similar to String but is slightly smaller and can't be resized without replacing it entirely. It's potentially more expensive to create though as converting a Sting to a Box implies trimming it, which may result in copying the string data)

Arc<str> (or Rc<str> if your structure will never be passed across threads) can be a good choice if your structure is likely to be copied, but it's a bit more expensive to create in the first place, because converting a String to an Arc requires copying the data.

&'static str is low overhead, but it means the strings must live for the remaining life of the program. So it's good if all your strings come from string literals, and it may be ok if your strings come from say a config file at program startup.

&'lifetime str is low overhead, but means the structure is only borrowing the strings, it doesn't own them and thus there is a need to seperately ensure that the strings outlive the structure. This can work well in some circumstances, for example if you load a data file into memory and then want to parse it without copying the actual data but most of the time it gets really annoying. An arena allocator like bumpalo can make it somewhat less annoying but it's still best used for short-lived data structures.

The documentation states that String is mutable. If I have a struct with, say, a name field that should be immutable,

The way mutability works in rust is fundamentally different from how it works in languages like Java and Python.

In python and traditionally in Java* every user-defined type implicitly has a shareable reference to it. There are no explicit lifetimes, the lifetime of everything is managed by the runtime. Since you can't prevent sharing, enforcing immutability at the type level becomes a common strategy to prevent accidents related to shared-mutablity.

In rust types are plain values with no implicit reference, mutability is less of a property of the type itself and more a property of how you access it. Normal types can only be mutated if you hold a "mutable reference" to them, and "mutable references" are normally mutually exclusive with shared references. The compiler tracks references to enforce this mutal-exclusivity (unless you use unsafe rust).

Even if the designer of the type provides no methods for mutation, if you have a mutable reference to the type, you can simply replace the whole damn thing.

There are some types that have "interior mutability", these types can be mutated even through a shared reference.

should I still use String instead of str?

Box<str> is slightly smaller than string in terms of the object itself. Converting a String to a Box<str> will also implicity trim the string, which may reduce heap memory usage, but may also cause an extra heap memory allocation and copy.

If I have a structure that includes the Copy attribute (eg #[derive(Clone, Copy)], then String doesn't compile. What is the best solution?

Types that own data on the heap generally can't implement Copy because they need to avoid double free.

If you can live with a relatively small length limit you might want to consider the ArrayString type from the arrayvec crate. This type stores the string data directly in the type, so it is Copy, and structs containing it can also be copy.

* There have been attempts in recent versions of Java to introduce true value types, but they were done in a strange way for backwards compatibility reasons.

1 Comment

Please edit your post to format types as code blocks. Otherwise generic parameters like in "Box<str>" just render as "Box".

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.