
- OsStr と OsString
- &OsStr の生成
- OsString の生成
- &OsStr と OsString の相互変換
- OsStr と str の相互変換
- OsString と String の相互変換
- std::env::Args の例
- std::fs::DirEntry の例
OsStr と OsString
- Rust の文字列は常に有効な Unicode であることが保証されている
- OSのファイル名やコマンドライン引数、環境変数などは有効な Unicode で表現できる保証がない
- 多くの Unix システムでは、文字列は非ゼロバイトの任意のシーケンスである(通常はUTF-8として解釈される)
- 多くの Windows では、文字列はゼロ以外の16ビット値の任意のシーケンスである(通常はUTF-16として解釈される)
- Unicode で表現できないOSの文字列を扱うために
std::ffi::OsStrとstd::ffi::OsStringがある(strとStringに対応する) std::ffi::OsStrとstd::ffi::OsStringは Unix であっても Windows であっても、8ビット値のシーケンスとして格納される(Windows では UTF-8 を拡張したエンコードが行われ、任意の 16 ビット 値が表現できる形でコード化される)
pub struct OsStr { inner: Slice, } pub struct Slice { pub inner: [u8], }
pub struct OsString { inner: Buf, } pub struct Buf { pub inner: Vec<u8>, }
&OsStr の生成
文字列から
let os_str: &OsStr = OsStr::new("foo");
OsString から
let os_string: OsString = OsString::from("foo"); let os_str: &OsStr = OsStr::new(&os_string);
バイト配列から
use std::os::unix::ffi::OsStrExt; let source = [0x66, 0x6f, 0x80, 0x6f]; let os_str: &OsStr = OsStr::from_bytes(&source[..]);
OsString の生成
let mut os_string: OsString = OsString::new(); os_string.push("foo");
OsString は From<String> を実装しているため、 文字列から OsString を作成できる。
let s: &str = "foo"; let os_string: OsString = s.into();
let string: String = String::from("foo"); let os_string: OsString = string.into();
let os_string: OsString = OsString::from("foo");
ベクタから
use std::os::unix::ffi::OsStringExt; let source: Vec<u8> = vec![0x66, 0x6f, 0x80, 0x6f]; let os_string: OsString = OsString::from_vec(source);
&OsStr と OsString の相互変換
&OsStr → OsString
let os_str: &OsStr = OsStr::new("foo"); let os_string: OsString = os_str.to_os_string();
OsString → &OsStr
let os_string: OsString = OsString::from("foo"); let os_str: &OsStr = &os_string;
let os_string: OsString = OsString::from("foo"); let os_str: &OsStr = os_string.as_os_str();
str と String の関係と同じ。
OsStr と str の相互変換
&OsStr → &str への変換は失敗する可能性がある。
let os_str: &OsStr = OsStr::new("foo"); let s: Option<&str> = os_str.to_str();
変換できない文字があった場合、U+FFFD に置き換える。これは必ず成功する。
let os_str: &OsStr = OsStr::new("foo"); let s: Cow<str> = os_str.to_string_lossy();
&str → &OsStr は必ず成功する。
let os_str: &OsStr = OsStr::new("foo");
OsString と String の相互変換
OsString → String への変換は失敗する可能性がある。
let os_string: OsString = OsString::from("foo"); let string: Result<String, OsString> = os_string.into_string();
String → OsString への変換は必ず成功する。
let os_string: OsString = string.into();
変換できない文字があった場合、U+FFFD に置き換える場合は、to_string_lossy() で Cow<str> として得ることができる。
let os_string: OsString = OsString::from("foo"); let str: Cow<str> = os_string.to_string_lossy();
std::env::Args の例
コマンドライン引数を例に見れば、
let mut args: std::env::Args = std::env::args(); let arg: Option<String> = args.next();
std::env::Args は OsString の IntoIter として表現されている。
pub struct Args { inner: ArgsOs, } pub struct ArgsOs { inner: sys::args::Args, } pub struct sys::args::Args { iter: vec::IntoIter<OsString>, }
args.next() で取得する際に String に変換されるため、OsString を意識することはあまりない。
impl Iterator for Args { type Item = String; fn next(&mut self) -> Option<String> { self.inner.next().map(|s| s.into_string().unwrap()) } }
std::fs::DirEntry の例
std::fs::read_dir() によるディレクトリの一覧取得。
let rd: std::fs::ReadDir = std::fs::read_dir("src")?; for entry in rd { let entry: DirEntry = entry?; let name: OsString = entry.file_name(); println!("{:?}", name); }
DirEntry から PathBuf やファイル名として OsString を取得できる。
impl DirEntry { pub fn path(&self) -> PathBuf { self.dir.root.join(self.file_name_os_str()) } pub fn file_name(&self) -> OsString { self.file_name_os_str().to_os_string() } }