Идиоматические преобразования для String, & str, Vec <u8> и & [u8]

Новый Rustacean, как я, борется с жонглированием этими типами: String, &str, Vec<u8>, &[u8].

Вовремя, я надеюсь, что у вас есть прозрение и вдруг выясните, почему некоторые вызовы библиотеки используют тот или иной. До тех пор мне нужна помощь в определении каждого идиоматического перехода.

Учитывая эти типы:

let st: &str = ...;
let s:  String = ...;
let u:  &[u8] = ...;
let v:  Vec<u8> = ...;

Я думаю, что я понял это, но они идиоматичны?

&str    -> String    String::from(st)
&str    -> &[u8]     st.as_bytes()
String  -> &str      s.as_str()
&[u8]   -> &str      str::from_utf8(u)
Vec<u8> -> String    String::from_utf8(v)

В конечном итоге я хочу получить полную таблицу переходов для этих типов:

&str    -> String
&str    -> &[u8]
&str    -> Vec<u8>
String  -> &str
String  -> &[u8]
String  -> Vec<u8>
&[u8]   -> &str
&[u8]   -> String
&[u8]   -> Vec<u8>
Vec<u8> -> &str
Vec<u8> -> String
Vec<u8> -> &[u8]

Ответ 1

С &str

  • &str -> String имеет много одинаково допустимых методов: String::from(st), st.to_string(), st.to_owned().
    • Но я предлагаю вам придерживаться одного из них в рамках одного проекта. Основное преимущество String::from заключается в том, что вы можете использовать его в качестве аргумента для метода map. Поэтому вместо x.map(|s| String::from(s)) вы часто можете использовать x.map(String::from).
  • &str&[u8] выполняется st.as_bytes()
  • &strVec<u8> является комбинацией &str -> &[u8] -> Vec<u8>, то есть st.as_bytes().to_vec() или st.as_bytes().to_owned()

Из String

  • String -> &str должен быть просто &s там, где принуждение доступно, или s.as_str() там, где его нет.
  • String -> &[u8] совпадает с &str -> &[u8]: s.as_bytes()
  • String -> Vec<u8> имеет собственный метод: s.into_bytes()

Из &[u8]

  • &[u8] -> Vec<u8> делается с помощью u.to_owned() или u.to_vec(). Они делают то же самое, но у to_vec есть небольшое преимущество: он недвусмысленно относится к типу, который он возвращает.
  • &[u8] -> &str на самом деле не существует, это будет &[u8] -> Result<&str, Error>, предоставленный через str::from_utf8(u)
  • &[u8] -> String является комбинацией &[u8] -> Result<&str, Error> -> Result<String, Error>

Из Vec<u8>

  • Vec<u8> -> &[u8] должен быть просто &v там, где доступно принуждение, или as_slice там, где его нет.
  • Vec<u8> -> &str совпадает с Vec<u8> -> &[u8] -> Result<&str, Error>, т.е. str::from_utf8(&v)
  • Vec<u8> -> String на самом деле не существует, это было бы Vec<u8> -> Result<String, Error> через String::from_utf8(v)

Принуждение доступно, когда цель не является общей, но явно указывается как &str или &[u8], соответственно. В Rustonomicon есть глава о принуждениях с более подробной информацией о сайтах принуждения.


ТЛ; др

&str    -> String  | String::from(s) or 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]   -> &str    | s.to_vec() or s.to_owned()
&[u8]   -> String  | std::str::from_utf8(s).unwrap(), but don't**
&[u8]   -> Vec<u8> | String::from_utf8(s).unwrap(), but don't**
Vec<u8> -> &str    | &s if possible* else s.as_slice()
Vec<u8> -> String  | std::str::from_utf8(&s).unwrap(), but don't**
Vec<u8> -> &[u8]   | String::from_utf8(s).unwrap(), but don't**

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

** handle the error properly instead