Skip to content
Select theme

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.

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:

hello hello hello

The 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.

42 true z

to_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:

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.

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:

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:

text text text text

That’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.

  • A &str going into a String parameter or field: String::from(s) is the plainest spelling, and s.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 String you already own and need to own again: clone(). to_string() allocates a second buffer too, but clone says that’s what you meant.