>>FIRST MISSION//LOGS//bytes//converting-rust-string-to-and-from.MD
[ + ] TARGET_LOCKED200 OK
SEP 22, 2021timer 3 MIN READSYS_TAG: bytes STATUS: VERIFIED_WORKING

Converting Rust String to and From

ENGINE: KERNEL // STABLE
BASE_ARCH: x86_64
HOST: VASTORIGINS.COM
GPG: OKSIG_ID: 0x4EB27DB0
Converting Rust String to and From
SPEC_IMAGE // PROVENANCE_VERIFIED

Rust strings confused me when I first started, and they confuse almost everyone who comes from languages where a string is just a string. In Rust, &str and String are different in a fundamental way: &str is a borrowed, immutable view of a string slice with a fixed size, while String is an owned, heap-allocated value that can grow dynamically and be modified. Most of the time you’ll be working with String in Rust when re-allocating and moving values between structs, since you cannot hand a borrowed view around forever without thinking about lifetimes.


There are times you may need to convert a dynamic string to character bytes and static strings, and the variety of methods can feel overwhelming at first. Here are the ways to do it, organized by what you are converting from:

From &str

  • &str -> String has many equally valid methods: String::from(st), st.to_string(), st.to_owned().
    • But I suggest you stick with one of them within a single project. The major advantage of String::from is that you can use it as an argument to a map method. So instead of x.map(|s| String::from(s)) you can often use x.map(String::from).
  • &str -> &[u8] is done by st.as_bytes()
  • &str -> Vec<u8> is a combination of &str -> &[u8] -> Vec<u8>, i.e. st.as_bytes().to_vec() or st.as_bytes().to_owned()

From String

  • String -> &str should just be &s where coercion is available or s.as_str() where it is not.
  • String -> &[u8] is the same as &str -> &[u8]: s.as_bytes()
  • String -> Vec<u8> has a custom method: s.into_bytes()

From &[u8]

  • &[u8] -> Vec<u8> is done by u.to_owned() or u.to_vec(). They do the same thing, but to_vec has the slight advantage of being unambiguous about the type it returns.
  • &[u8] -> &str doesn’t actually exist, that would be &[u8] -> Result<&str, Error>, provided via str::from_utf8(u)
  • &[u8] -> String is the combination of &[u8] -> Result<&str, Error> -> Result<String, Error>

From Vec<u8>

  • Vec<u8> -> &[u8] should be just &v where coercion is available, or v.as_slice() where it’s not.
  • Vec<u8> -> &str is the same as Vec<u8> -> &[u8] -> Result<&str, Error> i.e. str::from_utf8(&v)
  • Vec<u8> -> String doesn’t actually exist, that would be Vec<u8> -> Result<String, Error> via String::from_utf8(v)

Coercion is available whenever the target is not generic but explicitly typed as &str or &[u8], respectively. The Rustonomicon has a chapter on coercions with more details about coercion sites.


tl;dr

&str    -> String  | String::from(s), s.to_string(), or s.to_owned()
&str    -> &[u8]   | s.as_bytes()
&str    -> Vec<u8> | s.as_bytes().to_vec() or s.as_bytes().to_owned()
String  -> &str    | &s if possible* else s.as_str()
String  -> &[u8]   | s.as_bytes()
String  -> Vec<u8> | s.into_bytes()
&[u8]   -> Vec<u8> | s.to_vec() or s.to_owned()
&[u8]   -> &str    | str::from_utf8(s) -> Result<&str, _>; unwrap only if sure**
&[u8]   -> String  | String::from_utf8(s.to_vec()).unwrap(), but don't**
Vec<u8> -> &[u8]   | &v if possible* else v.as_slice()
Vec<u8> -> &str    | str::from_utf8(&v) -> Result<&str, _>; unwrap only if sure**
Vec<u8> -> String  | String::from_utf8(v).unwrap(), but don't**

* target should have explicit type (i.e., checker can't infer that)

** handle the error properly instead

That is the cheat sheet I keep coming back to. Bookmark it, and the next time Rust hands you a byte slice when you wanted a string, you will know exactly which conversion to reach for.

[ SIGNATURE VERIFIED // GPG 0x4EB27DB0 // PROVENANCE SECURE ]
➔ RETURN TO LOGS