参照外し*
の振る舞いをカスタマイズできる。
成果物
参考
- https://doc.rust-jp.rs/book/second-edition/ch15-02-deref.html
- https://doc.rust-lang.org/std/ops/trait.Deref.html
参照外し
main.rs
fn main() { let x = 5; let y = &x; assert_eq!(5, x); assert_eq!(5, *y); assert_eq!(5, y); }
変数y
は変数x
の参照である。その値を参照するために参照外し演算子*
を付与する。
もし*
を外してassert_eq!(5, y);
にしたら以下エラーになる。
$ rustc main.rs error[E0277]: can't compare `{integer}` with `&{integer}` --> main.rs:10:5 | 10 | assert_eq!(5, y); | ^^^^^^^^^^^^^^^^^ no implementation for `{integer} == &{integer}` | = help: the trait `std::cmp::PartialEq<&{integer}>` is not implemented for `{integer}` = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
Boxを参照のように使う
fn main() { let x = 5; let y = Box::new(x); assert_eq!(5, x); assert_eq!(5, *y); }
*
を付与したときも、しないときも、参照のときと同じ挙動。
独自のスマートポインタを定義する
struct MyBox<T>(T); impl<T> MyBox<T> { fn new(x: T) -> MyBox<T> { MyBox(x) } }
fn main() { let x = 5; let y = MyBox::new(x); assert_eq!(5, x); assert_eq!(5, *y); // assert_eq!(5, y); }
コンパイルエラー。「参照外しできない」と怒られる。
$ rustc main.rs error[E0614]: type `MyBox<{integer}>` cannot be dereferenced --> main.rs:15:19 | 15 | assert_eq!(5, *y); | ^^
Deref
トレイトを実装して参照外しする
use std::ops::Deref; struct MyBox<T>(T); impl<T> MyBox<T> { fn new(x: T) -> MyBox<T> { MyBox(x) } } impl<T> Deref for MyBox<T> { type Target = T; fn deref(&self) -> &T { &self.0 } } fn main() { let x = 5; let y = MyBox::new(x); assert_eq!(5, x); assert_eq!(5, *y); // assert_eq!(5, y); }
- 関連型:
type Target = T;
- 参照する値を返す:
&self.0
暗黙的な参照外し型強制
とは何か?
参照外し型強制は、 Derefを実装する型への参照をDerefが元の型を変換できる型への参照に変換します。
いつ発生する?
特定の型の値への参照を関数やメソッド定義の引数型と一致しない引数として関数やメソッドに渡すときに自動的に発生します。
たとえば先述までのコードを以下のように変える。
fn hello(name: &str) { println!("Hello, {}!", name); }
fn main() { let m = MyBox::new(String::from("Rust")); hello(&m); }
関数hello
の引数は&str
型。なのに渡されたのはMyBox<String>
。でも動く。「参照外し型強制」のおかげで。
疑問。String
型はstr
に変換できるから「参照外し型強制」の対象になるってこと? 他に変換できる型は?
もし参照外し型強制がなければ、以下のようなコードを書かねばならなかった。
fn main() { let m = MyBox::new(String::from("Rust")); hello(&(*m)[..]); }
疑問。上記コードで渡された型は&&str
になってhello
関数の引数&str
に一致しないのでは?
(*m)
で参照外しderef()
を実行して参照する値String
を返すString[..]
でスライス(&str
化)する&str
の参照&&str
をhello
に渡す
可変性
T: Deref<Target=U>
の時、&T
から&U
T: DerefMut<Target=U>
の時、&mut T
から&mut U
T: Deref<Target=U>
の時、&mut T
から&U
対象環境
- Raspbierry pi 3 Model B+
- Raspbian stretch 9.0 2018-11-13
- bash 4.4.12(1)-release
- rustc 1.34.2 (6c2484dc3 2019-05-13)
- cargo 1.34.0 (6789d8a0a 2019-04-01)
$ uname -a Linux raspberrypi 4.19.42-v7+ #1219 SMP Tue May 14 21:20:58 BST 2019 armv7l GNU/Linux
前回まで
- Rustを学んでみたい(プログラミング言語)
- Rustの環境構築
- RustでHelloWorld
- Rustの和訳ドキュメント
- Cargoでプロジェクト作成・ビルド・実行
- クレートとは?
- Rustで関数を使ってみる
- Rustでモジュールを使ってみる
- Rustで乱数を生成する(rand)
- Rustで標準入力する(std::io::stdin().read_line())
- RustでMatch判定する(match)
- Rustでprintとread_lineを1行にする方法
- Rustで数当てゲーム
- クレート名にドット.が使えない
- Rustの変数と可変性(let, mut) error[E0384]: cannot assign twice to immutable variable
x
- Rustのimmutable束縛とconst定数の違い
- RustのREPL、evcxrのインストールに失敗した
- Rustでコンパイルするときの変数未使用warningを消す
- Rustの変数(再代入、再宣言(シャドーイング))
- Rustのシャドーイングについて
- イミュータブルについて(副作用)
- Rustの定数(const)
- Rustのデータ型(数値)
- Rustのデータ型(論理)
- Rustのデータ型(文字)
- Rustのデータ型(タプル)
- Rustのデータ型(配列)
- Rustの関数
- Rustのif式
- Rustのくりかえし文(loop)
- Rustのくりかえし文(while)
- Rustのくりかえし文(for)
- Rustの所有権(ムーブ)
- Rustの所有権(関数)
- Rustの所有権(スライス)
- Rustの構造体(定義とインスタンス化)
- Rustの構造体(プログラム例)
- Rustの構造体(メソッド)
- Rustの列挙型(enum)
- Rustの列挙型(enum)
- Rustの列挙型(enum)
- Rustのmatch(制御フロー演算子)
- RustでNULLを扱う(Option, Some, None)
- NULL参照は10億ドルの失敗だった
- Rustの列挙型に独自表示を実装する(E0277 対策 std::fmt::Display 実装)
- RustのIfLet(matchの糖衣構文)
- Rustのプロジェクト構造
- Rustのcargoでライブラリ&テスト(単体、結合)
- Rustのモジュール(mod)
- Rustのモジュール(pub)
- Rustのmod参照方法(
mod 子モジュール名;
,use 要素名;
,extern crate クレート名;
,super
) - Rustのインポートまとめ(Rust2018)
- RustのコレクションVec型
- RustのコレクションString型
- RustのコレクションHashMap型
- Rustのコレクション(練習問題)
- Rustのエラー処理
- Rustのジェネリクス
- Rustのトレイト
- Rustのライフタイム1
- Rustのライフタイム2(構造体の定義)
- Rustのライフタイム3(ライフタイム省略)
- Rustのライフタイム4(impl定義)
- Rustの静的ライフタイム5('static)
- Rustのライフタイム6(ジェネリクス、トレイト境界とともに)
- Rustのテストコードを書く
- Rustのテスト実行
- Rustのテスト体系化
- Rustでコマンドライン引数を受け取る
- Rustのファイル読込
- Rustでリファクタリング(モジュール性とエラー処理の向上)
- Rustでテスト駆動開発
- Rustで環境変数を取得する
- RustでStdErr出力
- Rustのクロージャ
- Rustのイテレータ
- Rustのイテレータ(Minigrep改善)
- Rustのイテレータ(パフォーマンス)
- Rustのイテレータ(Minigrep改善)
- Rustのcargo(ビルドのカスタマイズ)
- Rustのcargo(cargo docでドキュメント生成)
- Rustのエクスポート(pub use)
- Rustのクレートを公開する方法(crates.io)
- Rustのcargoでワークスペースをつくる
- Rustのcargo installでバイナリをインストールする
- Rustのcargoを拡張する方法
- Rustのスマートポインタ
- スマートポインタBox