When to use String::from, to_string, or into?
Short answer: All three spellings turn a &str into an owned String. String::from names the
source, to_string asks for the text form of a value, and into lets the destination drive the
conversion.
Three spellings, one result
Section titled “Three spellings, one result”From a fresh project — cargo new string-demo, then replace src/main.rs with the whole of this.
The project name doesn’t matter; nothing in the output depends on it:
fn main() { let a = String::from("hello"); let b = "hello".to_string(); let c: String = "hello".into();
println!("{a} {b} {c}");}hello hello helloThe result is the same String either way. String::from and into are two spellings of one
From<&str> impl; to_string takes the other route, the Display blanket impl, and lands on the
same String. For a slice the choice is about reading, not behaviour: pick the spelling that says
what you mean where it sits.
to_string when the value isn’t text
Section titled “to_string when the value isn’t text”fn main() { let n = 42.to_string(); let ok = true.to_string(); let c = 'z'.to_string();
println!("{n} {ok} {c}");}42 true zto_string comes from the Display trait, so every printable type has it: integers, floats, bools,
char, and your own types once they implement Display. String::from can’t reach them:
fn main() { let s = String::from(42); println!("{s}");}error[E0277]: the trait bound `String: From<{integer}>` is not satisfied --> src/main.rs:2:13 |2 | let s = String::from(42); | ^^^^^^ the trait `From<{integer}>` is not implemented for `String` | = help: `String` implements trait `From<T>`: From<&String> From<&mut str> From<&str> From<Box<str>> From<Cow<'_, str>> From<char>
For more information about this error, try `rustc --explain E0277`.The help line is the full list: &str, &String, &mut str, Box<str>, Cow<str> and char.
When the value is already text, both spellings work. When it isn’t, to_string is the one that
does.
into when the destination is known
Section titled “into when the destination is known”fn greet(name: String) { println!("Hello, {name}!");}
fn main() { let name = "Ada"; greet(name.into());}Hello, Ada!into takes its target type from the surroundings — a parameter type, a let annotation, a return
type. Where that type is visible, into keeps the call site short. Where it isn’t, the compiler
refuses to guess:
fn main() { let s = "hello".into(); println!("{s}");}error[E0283]: type annotations needed --> src/main.rs:2:9 |2 | let s = "hello".into(); | ^ ---- type must be known at this point | = note: the type must implement `From<&str>` = note: required for `&str` to implement `Into<_>`help: consider giving `s` an explicit type |2 | let s: /* Type */ = "hello".into(); | ++++++++++++
For more information about this error, try `rustc --explain E0283`.The note names the reason: the destination has to implement From<&str>, and more than one type
does, so there is no single answer to infer. Naming the type fixes it — and the same &str can land
in any of them:
use std::borrow::Cow;use std::rc::Rc;
fn main() { let owned: String = "text".into(); let boxed: Box<str> = "text".into(); let cow: Cow<str> = "text".into(); let rc: Rc<str> = "text".into();
println!("{owned} {boxed} {cow} {rc}");}text text text textThat’s the intent of into: the destination drives, and the source stays out of the way. It isn’t
tied to &str either — the E0277 list above shows every source String accepts, including char.
Which one to reach for
Section titled “Which one to reach for”- A
&strgoing into aStringparameter or field:String::from(s)is the plainest spelling, ands.into()is fine wherever the parameter type is right there. - A number, bool or
char:to_string(). - Text with other text around it:
format!— the subject of the next article in this chapter. - A
Stringyou already own and need to own again:clone().to_string()allocates a second buffer too, butclonesays that’s what you meant.
Related
Section titled “Related”- What is the difference between String and &str? — the owned and borrowed split this page builds on
- Why does my String not implement Copy? — why every spelling above allocates
- Working with text — the rest of this chapter
- Next in this chapter: how to build a string from parts