参照型 &T
参照型 &T
値を所有していないことを表す型。
&演算子(Borrow演算子)によって生成する。
借用
Rustでは、&演算子によって参照型を生成することを「値を借用する」という。
let a = String::from("a");
// 変数aの値を借用する
let b: &String = &a;
値のライフタイム
値の所有権を持たないため、参照型の変数のスコープは、値のスコープを超えることはできない。
let result: &String = match input {
// 値のスコープを超える参照を返せない
"1" => &String::from("Hello World"),
_ => panic!("You occur system down"),
}
/*
error[E0716]: temporary value dropped while borrowed
|
5 | let result = match input {
| ------ borrow later stored here
6 | "1" => &String::from("Apple"),
| ^^^^^^^^^^^^^^^^^^^^-
| | |
| | temporary value is freed at the end of this statement
| creates a temporary which is freed while still in use
*/
値のライフタイムの付与
関数の返り値や型のメンバ変数に参照型を取る場合、値のライフタイムを手動で付ける必要がある。
参照型を返す場合
// 返す参照型のライフタイムは引数で受け取った値と同じことを保証する
fn max<'a>(str1: &'a str, str2: &'a str) -> &'a str {
if str1.len() > str2.len() {
str1
} else {
str2
}
}
参照型をメンバ変数としてもつ場合
struct Foo<'a> {
val: &'a i32,
}
コピーセマンティクス
型Tに関わらず、Copyトレイトを実装しているので、参照型はコピーセマンティクスとなる。
let mut origin = String::from("Origin");
let ref1 = &origin;
let ref2 = &ref1; // 参照がコピーされる
let ref3 = &ref2; // 参照がコピーされる
実装しているトレイト
CopyCloneTがCloneを実装しているしていないに関わらずDeref*演算子と自動参照外しBorrowPointer
Tが実装している場合
PartialOrdOrdPartialEqEqAsRefFnHash
参照
References and Borrowing - The Rust Programming Language
関連
自動参照外し (Auto-dereferencing)
元ネタ
https://sodocumentation.net/rust/topic/2574/auto-dereferencing
The dot operator ドット演算子
.演算子によって、コンパイル時に必要なだけの*が挿入され、自動で参照外しが行われる。
Deref型強制の一種。
let mut name: String = "Hello World".to_string();
// derefされない
name.push('!');
let name_ref = &name; // len()メソッドは、参照型の&Stringには実装されていないが、自動参照外し(Auto deref)によって呼び出しが解決される let name_len = name_ref.len(); // 本来は参照元のメソッドを呼び出すには、参照外しが必要となる let name_len2 = (*name_ref).lne();
値のラッピングに対するDerefの実装
ラッピングした値を返すDerefトレイトを実装することで、 コンパイル時に型変換することができる。
use std::ops::Deref;
use std::fmt::Debug;
// Optionをラッパー
#[derive(Debug)]
struct RichOption<T>(Option<T>);
// 参照外しされた際、ラッパーしたOptionを返す
impl<T> Deref for RichOption<T> {
type Target = Option<T>;
fn deref(&self) -> &Option<T> {
&self.0
}
}
impl<T: Debug> RichOption<T> {
fn print_inner(&self) {
println!("{:?}", self.0)
}
}
fn main() {
let x = RichOption(Some(1));
// Optionのメソッドが呼び出せる
println!("{:?}",x.map(|x| x + 1));
// 参照外しによってOptionに変換できる
fn_that_takes_option(&x);
// RichOptionに実装されたメソッドも呼び出せる
x.print_inner()
}
fn fn_that_takes_option<T : std::fmt::Debug>(x: &Option<T>) {
println!("{:?}", x)
}
関連
アンチパターン Deref ポリモフィズム - あるマのメモ書き
Appendix
B - Operators and Symbols - The Rust Programming Language
| 演算子 | 例 | 説明 |
|---|---|---|
* |
*expr |
Dereference |
& |
&expr, &mut expr |
Borrow |
パターンマッチ ".." と "_"の違い
元ネタ
Difference between ".." and "_" in match patterns. : rust
_一つの値のみにマッチする..複数の値にマッチする
let v = (1, 2, 3);
match v {
(1, 2, _) => println!("1, 2, _"),
(1, ..) => println!("1, .."),
(..) => println!(".."),
}
Option<T>
Option
enum Option<T> {
None,
Some(T),
}
boolに変換
is_some(&self)is_none(&self)
値の取り出し
T
expect(self), msg: &str) -> Tunwrap(self) -> Tunwrap_or(self, default: T) -> Tunwrap_or_else<F>(self, f: F) -> T where F: FnOnce() -> T
Result<T, E>
ok_or<E>(self, err: E) -> Result<T, E>ok_or_else<E, F>(self, err: F) -> Result<T, E> where F: FnOnce() -> E
let num = x.ok_or_else(|| 0);
Option<&T>
as_ref(&self) -> Option<&T>as_mut(&mut self) -> Option<&mut T>
let text_length: Option<usize> = text.as_ref().map(|s| s.len());
// 中身の書き換え
match x.as_mut() {
Some(v) => *v = 42,
None => {}
}
or(self, optb: Option<T>) -> Option<T>or_else<F>(self, f: F) -> Option<T> where F: FnOnce() -> Option<T>xor(self, optb: Option<T>) -> Option<T>
let v = x.or(Some(0));
fn nobody() -> Option<&'static str> { None }
fn vikings() -> Option<'static str> { Some("vikings") }
let l = Some("barbarians").or_else(vikings)
let m = None.or_else(vikings) // Some("vikings")
let n = None.or_else(noobody) // None
ユーティリティ
値型の変更
map(self, f: F) -> Option<U>map_or(self, default: U, f: F) -> U where F: FnOnce(T) -> Umap_or_else(self, default: D, f: F) -> U where D: FnOnce() -> U, F: FnOnce(T) -> U