参照型 &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; // 参照がコピーされる
実装しているトレイト
Copy
Clone
T
がCloneを実装しているしていないに関わらずDeref
*
演算子と自動参照外しBorrow
Pointer
T
が実装している場合
PartialOrd
Ord
PartialEq
Eq
AsRef
Fn
Hash
参照
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) -> T
unwrap(self) -> T
unwrap_or(self, default: T) -> T
unwrap_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) -> U
map_or_else(self, default: D, f: F) -> U where D: FnOnce() -> U, F: FnOnce(T) -> U
所有権と借用
所有権 Ownership
- 所有権とは
- 所有権と値のライフスコープ
- 所有権の移動
- コピーセマンティクス
- Copyトレイト実装型
- 基本型 (bool, i32, ...)
- 参照型
- Copyトレイト実装型
- ムーブセマンティクス
- コピーセマンティクス
所有権とは
値を保持できる変数は、必ず1つであるという制約のこと。
#[derive(Debug)] struct V(i32); // 変数aが値V(0)の所有者となる let a = V(0); // 変数bにaを代入すると、値V(0)の所有者が変数bに移る let b = a; // コンパイルエラー dbg!(a);
所有権と値のライフスコープ
「値の所有者は1つである」という制約から所有する変数のスコープが値のライフタイムとなる。
struct V; let a = V; { let b = a; // 値Vのライフタイム = 変数bのスコープ }
所有権の移動
代入によって所有権が移動するかどうかは、Copyトレイトを実装しているかどうかによって決まる。
Copyトレイト
Cloneのサブトレイトであり、マーカートレイト。
#[derive(Debug)] #[derive(Clone, Copy)] struct V; let a = V; { // 値がCopyされるので、aは所有権を失わない let b = a; } // aは有効 dbg!(a);
主なCopyトレイト実装型
- 基本型
- 参照型
&T
参照型&T
はコピーセマンティクス
fn get_str(str: &str) { let str1 = str; // strがコピーされる let str2 = str1; // str1がコピーされる dbg!(str2); // OK dbg!(str1); // OK dbg!(str); // OK }
値の借用 Borrow
&
演算子(Borrow)によって、参照型(&T
)を生成することを「値を借用する」という。
このとき、値の所有権が移動しない。
&
演算子(Borrow)は、参照型を生成する。
let a = String::from("Hello World"); // &演算子によって参照型を生成する => 変数aの値を借用する => 所有権は移動しない let ref_a: &String = &a;
借用ルール
- mutable参照は一度に1つまでしか作れない
- mutable参照が存在する場合は、借用できない
fn main() { let mut s = String::from("Hello"); let mut ref_s = &mut s; // NG let mut ref_s2 = &mut s; ref_s.push(' '); ref_s2.push_str("World"); /* error[E0499]: cannot borrow `s` as mutable more than once at a time | 3 | let mut ref_s = &mut s; | ------ first mutable borrow occurs here ... 6 | let mut ref_s2 = &mut s; | ^^^^^^ second mutable borrow occurs here 7 | 8 | ref_s.push(' '); | ----- first borrow later used here */ }
参照
References and Borrowing - The Rust Programming Language
関連
ポリモーフィズム with トレイトオブジェクト
ポリモーフィズム (Polymophism)
deepLによる翻訳
ポリモーフィズム 多くの人にとって、ポリモーフィズムは継承と同義語です。しかし、実際にはもっと一般的な概念であり、複数の型のデータを扱うことができるコードを指します。継承の場合、それらの型は通常サブクラスです。
その代わりに、Rust はジェネリックを使ってさまざまな可能性のある型を抽象化し、 trait bounds を使ってそれらの型が提供しなければならないものに制約を与えます。これを bounded parametric polymorphism と呼ぶこともあります。
参照
Object Oriented Programming Features of Rust - The Rust Programming Language