概要
Rc::as_ref() の入れ所がよく分からなくなってきたので、作ったもの。
環境
- Ubuntu 18.04 LTS
- rust 1.58
解説
as_ref()を使わない場合
例1: sを消費して中身を取り出す
fn main() {
let s: Option<String> = Some(String::from("hello"));
let t: String = s.unwrap().push_str(" world!");
assert_eq!(t, "hello world!");
}
例2: unwrap() は消費する
次の例は、unwrap で所有権の移動が起こるので、コンパイルエラーとなる。
fn main() {
let s = Some(String::from("hello"));
if s.unwrap().len() == 5 { // move
let mut t = s.unwrap();
// ^ value used here after move
t.push_str(" world!");
assert_eq!(t, "hello world!");
}
}
例3: as_ref()を使う
Option::as_ref()
は、&Option<T>
を Option<&T>
に変換します。
&String
のように借用するのであれば、消費はされません。
-
s.as_ref()
: Some("hello") → Some(&"hello") -
s.as_ref().unwrap()
: Some(&"hello") → &"hello" -
s.as_ref().unwrap().len()
: &"hello" → &"hello".len()
※ String は省略しています
fn main() {
let s = Some(String::from("hello"));
if s.as_ref().unwrap().len() == 5 {
let mut t = s.unwrap();
t.push_str(" world!");
assert_eq!(t, "hello world!");
}
}
応用例: as_mut()
-
self.msg.as_mut()
の部分がself.msg
の場合に失敗するのは上で説明した通り、move されるからです -
self.msg.as_ref()
では、可変の借用ではないのでString::push_str()
は使えません -
Option::as_mut()
は mut Option(T) → Option(&mut T) に置き換えます -
&mut String
なら、push_str() は使えます。
struct Msg {
msg: Option<String>,
}
impl Msg {
pub fn push_str_in_option(&mut self, a: &str) {
if let Some(msg) = self.msg.as_mut() {
msg.push_str(a);
}
}
}
fn main() {
let mut s = Msg { msg: Some(String::from("hello")) };
s.push_str_in_option(" world!");
assert_eq!(s.msg, Some(String::from("hello world!")));
let mut s = Msg { msg: None };
s.push_str_in_option(" world!");
assert_eq!(s.msg, None);
}
次のようにも書ける。
Option::map(f)
で Option(&T)
→ Option(f(&T))
とできる。
//...
impl Msg {
pub fn push_str_in_option(&mut self, a: &str) {
self.msg.as_mut().map(|s| s.push_str(a));
}
}
//...