Question
How can I define a Rust struct whose string fields are owned by the struct, and how can I create instances of it? Please show the correct struct declaration and instantiation examples.
Short Answer
You will learn how to store owned text in a Rust struct with String, initialize its fields, update them safely, and choose between owned String values and borrowed &str references.
Concept
A Rust struct groups related values into one named type. For text that should belong to the struct itself, use the owned String type:
struct User {
name: String,
email: String,
}
A String owns its text data. When a User value owns a String, the text remains valid for as long as that User exists. When the User is dropped, Rust automatically drops its String fields too.
This matters because &str is usually a borrowed reference to text owned somewhere else. A struct containing &str fields must specify how long those references remain valid with a lifetime annotation. Using String avoids that lifetime requirement when the struct should own the text.
Use String when:
- The struct must keep text after the caller's original variable is gone.
- The text may need to change later.
- You are building values from user input, JSON, files, databases, or network requests.
Use &str only when the struct intentionally borrows text that is guaranteed to outlive it.
Mental Model
Think of a struct as a backpack.
- A
Stringfield is an item placed inside the backpack. The backpack owns it and carries it wherever it goes. - A
&strfield is a note with directions to an item stored somewhere else. The note is useful only while the original item still exists.
If the struct needs to keep its own copy of text, put a String inside the backpack.
Syntax and Examples
Use String as the field type, then create String values with String::from(...), .to_string(), or other APIs that produce a String.
struct Book {
title: String,
author: String,
pages: u32,
}
fn main() {
let book = Book {
title: String::from("The Rust Book"),
author: "Rust Project Developers".to_string(),
pages: 560,
};
println!("{} by {}", book.title, book.author);
}
title and author are owned by book. The string literals in the source code have type &'static str, so they are converted into owned String values during construction.
A constructor method can make creation cleaner when a struct has several owned fields:
struct User {
name: String,
email: String,
}
impl User {
fn new(name: impl Into<String>, email: impl Into<String>) -> Self {
Self {
name: name.into(),
email: email.into(),
}
}
}
fn main() {
let user = User::new("Ava", "ava@example.com");
println!("{}: {}", user.name, user.email);
}
impl Into<String> lets callers pass either string literals such as "Ava" or existing String values.
Step by Step Execution
Consider this program:
struct Profile {
username: String,
bio: String,
}
fn main() {
let username = String::from("mira");
let profile = Profile {
username,
bio: String::from("Learning Rust"),
};
println!("{}", profile.username);
}
Step by step:
String::from("mira")allocates an owned string and binds it tousername.- The
Profileinstance is created. - Writing
username,uses field-init shorthand because the variable and field have the same name. - The
Stringis moved from the local variable intoprofile.username. profilenow owns bothusernameandbio.- The
println!call borrowsprofile.usernamelong enough to display it. - At the end of
main,profileis dropped, followed by its ownedStringfields.
After step 4, this would not compile because username was moved:
// println!("{username}"); // error: value borrowed here after move
If you need to keep the original string too, clone it deliberately:
let username = String::from("mira");
let profile = Profile {
username: username.clone(),
bio: String::from("Learning Rust"),
};
println!("{username}");
Real World Use Cases
Owned String fields are common when data needs to live independently from the code that created it.
- API requests: Store a user's name, email address, or message parsed from JSON.
- Database models: Represent columns such as product names, titles, and descriptions.
- CLI applications: Keep command-line arguments and configuration values after parsing.
- File processing: Store lines, paths, and report content read from a file.
- Domain models: Model entities such as
Customer,Order,Article, orGameCharacterwith text properties.
For example, deserialized request data is often owned:
struct CreateAccountRequest {
username: String,
password: String,
}
The request value can safely move through validation and business-logic functions without depending on the original input buffer.
Real Codebase Usage
In real Rust projects, structs commonly own data at boundaries: after reading input, parsing a response, or loading database records. Ownership makes it easier to return, store, queue, and pass values between functions safely.
Validate borrowed input, then create an owned model
struct Account {
username: String,
}
fn create_account(username: &str) -> Result<Account, String> {
let username = username.trim();
if username.is_empty() {
return Err(String::from("Username cannot be empty"));
}
Ok(Account {
username: username.to_owned(),
})
}
This pattern accepts a borrowed &str so the caller does not need to allocate first. After validation, .to_owned() creates the String that the returned Account owns.
Update a string field
struct Document {
title: String,
}
fn rename(document: &mut Document, new_title: &str) {
document.title = new_title.to_owned();
}
Use &mut Document when changing a field. The assignment drops the old String and replaces it with the new owned value.
Accept flexible text in constructors
Constructors often use impl Into<String> to accept both String and &str, while still storing a String internally.
Common Mistakes
Using str instead of String
str is an unsized text type, so it generally cannot be stored directly as a normal struct field.
// Does not compile
struct User {
name: str,
}
Use String for owned text:
struct User {
name: String,
}
Assigning a string literal directly to a String field
A string literal is &'static str, not String.
// Does not compile
let user = User {
name: "Ava",
};
Convert it:
let user = User {
name: String::from("Ava"),
};
Trying to use a value after moving it into the struct
let name = String::from("Ava");
let user = User { name };
// println!("{name}"); // does not compile: `name` was moved
Access it through user.name, borrow it before moving, or use name.clone() if two independent owners are truly needed.
Borrowing a temporary string
// Do not try to store a reference to temporary generated text.
// The referenced value would be dropped too soon.
If the struct needs generated text, store it as a String.
Adding Clone automatically
Cloning works but may allocate and copy data. Prefer moving a String into the struct when the caller no longer needs it. Clone only when separate ownership is required.
Comparisons
| Type | Owns text? | Can the text change? | Lifetime annotation needed in a struct? | Typical use |
|---|---|---|---|---|
String | Yes | Yes | No | Struct owns user input or parsed data |
&str | No | No through that reference | Usually yes | Struct temporarily views existing text |
&String | No | No through that reference | Yes | Rarely needed; prefer &str for read-only text |
Cow<'a, str> | Sometimes | Can become owned | Yes | Avoid an allocation unless modification is necessary |
For a first version of a data model, String is often the simplest choice when ownership is required.
String and &str are related but are not interchangeable:
let owned: String = String::from("hello");
let borrowed: &str = &owned;
Rust can borrow a String as &str, but it cannot turn an arbitrary &str into a String without creating an owned copy using .to_owned() or String::from(...).
Cheat Sheet
// Define owned text fields
struct Person {
name: String,
city: String,
}
// Create an instance from literals
let person = Person {
name: String::from("Noah"),
city: "Lisbon".to_string(),
};
// Create a String from a &str
let text = "hello".to_owned();
// Move an existing String into a field
let name = String::from("Noah");
let person = Person {
name,
city: String::from("Lisbon"),
};
// Borrow a field without moving it
println!("{}", person.name);
// Mutate a field
let mut person = person;
person.city.push_str(", Portugal");
// Constructor that accepts &str or String
fn new_name(name: impl Into<String>) -> String {
name.into()
}
Rules:
- Use
Stringwhen the struct owns text. - Use
String::from("text"),"text".to_string(), or"text".to_owned()to convert literals toString. - Moving a
Stringinto a struct transfers ownership. - Mark the struct instance
mutbefore changing one of its fields. - Use
&strparameters when a function only needs to read text temporarily.
FAQ
Should a Rust struct use String or &str?
Use String when the struct should own and retain the text. Use &str when it only borrows text owned elsewhere and its lifetime is clear.
Why does name: "Ada" not compile for a String field?
"Ada" is a string slice (&str). Convert it with String::from("Ada"), "Ada".to_string(), or "Ada".to_owned().
Does moving a String into a struct copy the text?
No. A move transfers ownership of the String value without copying its text buffer. Cloning is the operation that copies the text.
Can I change a String field after constructing the struct?
Yes, if the struct instance is mutable:
let mut user = User { name: String::from("Ada") };
user.name = String::from("Grace");
Why do structs with &str fields need lifetimes?
The struct must not outlive the text it references. Lifetimes let Rust verify that the referenced text stays valid long enough.
Is .to_string() different from String::from() for string literals?
Both create an owned String for ordinary string literals. String::from("text") clearly signals conversion to String; .to_string() works for many types that implement Display.
Can a struct own some fields and borrow others?
Yes. For example, a struct can contain name: String and source: &'a str. The struct then needs a lifetime parameter for the borrowed field.
Mini Project
Description
Build a small contact record that owns a person's name, email address, and optional note. This mirrors data gathered from a form, command-line input, or an API request, where the resulting struct must keep its data independently.
Goal
Create, validate, display, and update a contact using owned String fields.
Requirements
- Define a
Contactstruct with ownedStringfields fornameandemail. - Add an optional owned note using
Option<String>. - Create a constructor that accepts text input and stores owned strings.
- Reject an empty name or an email without
@. - Add a method that updates the contact note.
- Print the final contact details.
Keep learning
Related questions
Absolute Values in Rust: Using the abs() Method
Learn how to calculate absolute values in modern Rust with abs(), handle signed integer overflow, and choose safer alternatives.
Accepting Iterators in Rust Functions with impl Iterator
Learn how to write Rust functions that accept HashMap iterators using generic bounds or impl Iterator, avoiding trait object and lifetime errors.
Access Enum Variant Values in Rust with Pattern Matching
Learn how to extract and print values stored in Rust enum variants using match and if let pattern matching, with practical examples.