文字列型変換ガイド
Rustの文字列型の変換方法をまとめました。
文字列型の種類
| 型 | 説明 | 所有権 |
|---|---|---|
String | ヒープ上の可変文字列 | あり |
&str | 文字列スライス(参照) | なし |
&String | Stringへの参照 | なし |
&'static str | 静的な文字列リテラル | なし |
変換早見表
.to_string()
┌────────────────────┐
│ ▼
&str ◄──────────────── String
│ &s, .as_str() │
│ │
│ │
.to_owned() .clone()
│ │
▼ ▼
String String
&str → String
#![allow(unused)]
fn main() {
// 方法1: to_string()
let s: &str = "hello";
let string: String = s.to_string();
// 方法2: to_owned()
let string: String = s.to_owned();
// 方法3: String::from()
let string: String = String::from(s);
// 方法4: into()
let string: String = s.into();
}
どれを使うべき?
to_string(): 最も一般的to_owned(): 「所有権を得る」という意図が明確String::from(): 型変換であることが明確into(): ジェネリクスで便利
String → &str
#![allow(unused)]
fn main() {
let s: String = String::from("hello");
// 方法1: &演算子(自動deref)
let slice: &str = &s;
// 方法2: as_str()
let slice: &str = s.as_str();
// 方法3: スライス構文
let slice: &str = &s[..];
}
どれを使うべき?
&s: 最もシンプルas_str(): 意図が明確
数値 → String
#![allow(unused)]
fn main() {
// 方法1: to_string()
let n: i32 = 42;
let s: String = n.to_string();
// 方法2: format!マクロ
let s: String = format!("{}", n);
// フォーマット指定
let hex: String = format!("{:x}", 255); // "ff"
let padded: String = format!("{:05}", 42); // "00042"
}
String → 数値
#![allow(unused)]
fn main() {
let s: &str = "42";
// 方法1: parse()
let n: i32 = s.parse().unwrap();
// 方法2: parse()(型を明示)
let n = s.parse::<i32>().unwrap();
// エラーハンドリング
match s.parse::<i32>() {
Ok(n) => println!("数値: {}", n),
Err(e) => println!("パースエラー: {}", e),
}
}
char → String
#![allow(unused)]
fn main() {
let c: char = 'A';
// 方法1: to_string()
let s: String = c.to_string();
// 方法2: String::from()
let s: String = String::from(c);
// 方法3: format!
let s: String = format!("{}", c);
}
String → char
#![allow(unused)]
fn main() {
let s: String = String::from("A");
// 最初の文字を取得
let c: char = s.chars().next().unwrap();
// すべての文字をイテレート
for c in s.chars() {
println!("{}", c);
}
}
Vec ↔ String
#![allow(unused)]
fn main() {
// String → Vec<u8>
let s = String::from("hello");
let bytes: Vec<u8> = s.into_bytes();
// Vec<u8> → String(UTF-8として解釈)
let bytes = vec![104, 101, 108, 108, 111]; // "hello"
let s: String = String::from_utf8(bytes).unwrap();
// 失敗する可能性がある場合
match String::from_utf8(bytes) {
Ok(s) => println!("{}", s),
Err(e) => println!("Invalid UTF-8: {}", e),
}
// lossy変換(無効なバイトは置換)
let s = String::from_utf8_lossy(&bytes);
}
&[u8] → &str
#![allow(unused)]
fn main() {
let bytes: &[u8] = b"hello";
// UTF-8として解釈
let s: &str = std::str::from_utf8(bytes).unwrap();
// または
match std::str::from_utf8(bytes) {
Ok(s) => println!("{}", s),
Err(e) => println!("Invalid UTF-8: {}", e),
}
}
PathBuf ↔ String
#![allow(unused)]
fn main() {
use std::path::PathBuf;
// String → PathBuf
let s = String::from("/path/to/file");
let path = PathBuf::from(&s);
// PathBuf → String
let path = PathBuf::from("/path/to/file");
let s: String = path.to_string_lossy().into_owned();
// PathBuf → &str(失敗する可能性あり)
if let Some(s) = path.to_str() {
println!("{}", s);
}
}
OsString ↔ String
#![allow(unused)]
fn main() {
use std::ffi::OsString;
// String → OsString
let s = String::from("hello");
let os_string = OsString::from(&s);
// OsString → String
let os_string = OsString::from("hello");
match os_string.into_string() {
Ok(s) => println!("{}", s),
Err(os_string) => println!("Invalid UTF-8"),
}
// lossy変換
let s = os_string.to_string_lossy().into_owned();
}
CString ↔ String(FFI用)
#![allow(unused)]
fn main() {
use std::ffi::{CString, CStr};
// String → CString
let s = String::from("hello");
let c_string = CString::new(s).unwrap(); // NULLバイトがあるとエラー
// CString → String
let c_string = CString::new("hello").unwrap();
let s: String = c_string.into_string().unwrap();
// &CStr → &str
let c_str: &CStr = c_string.as_c_str();
let s: &str = c_str.to_str().unwrap();
}
よくある変換パターン
関数の引数として
#![allow(unused)]
fn main() {
// &strを受け取る(推奨)
fn process(s: &str) {
// String も &str も渡せる
}
// 使用
process("literal"); // &str
process(&String::from("s")); // &String → &str(自動deref)
process(String::from("s").as_str()); // 明示的
}
構造体のフィールドとして
#![allow(unused)]
fn main() {
// 所有権が必要な場合
struct User {
name: String, // 所有する
}
// 参照でよい場合(ライフタイム必要)
struct UserRef<'a> {
name: &'a str,
}
}
戻り値として
#![allow(unused)]
fn main() {
// 新しい文字列を作る場合
fn create_greeting(name: &str) -> String {
format!("Hello, {}!", name)
}
// 入力の一部を返す場合(ライフタイム必要)
fn first_word(s: &str) -> &str {
&s[..s.find(' ').unwrap_or(s.len())]
}
}
まとめ
| 変換 | 方法 |
|---|---|
&str → String | .to_string(), .to_owned() |
String → &str | &s, .as_str() |
数値 → String | .to_string(), format!() |
String → 数値 | .parse() |
Vec<u8> → String | String::from_utf8() |
String → Vec<u8> | .into_bytes() |
基本方針:
- 関数の引数は
&strで受け取る(柔軟性) - 所有権が必要なら
Stringを使う - 変換が必要な時は目的に合った方法を選ぶ