隻眼の少女

単行本発売日: 2010年
著者: 麻耶 雄嵩 (まやゆたか)

まさかの結末。まさかの復讐劇。

キャッチコピーの「ここまで恐ろしいヒロイン」という意味を知りたくてディープに読みすすめてしまったせいか結末のショックがつらすぎた。

硝子の塔の殺人(知念実希人)を読んでいなければ、 しばし本格ミステリーは読む気すらしないほど、落ち込んでしまったかもしれない。

2011年 第64回日本推理作家協会賞(長編及び連作短編集部門)と第11回本格ミステリ大賞をダブル受賞した作品とのこと。

参照型 &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

参照

reference - Rust

References and Borrowing - The Rust Programming Language

関連

参照型とポインタ型 - あるマのメモ書き

自動参照外し (Auto-dereferencing) - あるマのメモ書き

所有権と借用 - あるマのメモ書き

自動参照外し (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型 - あるマのメモ書き

アンチパターン Deref ポリモフィズム - あるマのメモ書き

Deref in std::ops - Rust

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, ...)
        • 参照型
    • ムーブセマンティクス

所有権とは

値を保持できる変数は、必ず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

関連

参照型 &T - あるマのメモ書き

ポリモーフィズム with トレイトオブジェクト

ポリモーフィズム (Polymophism)

ポリモーフィズム in Rust

deepLによる翻訳

ポリモーフィズム 多くの人にとって、ポリモーフィズムは継承と同義語です。しかし、実際にはもっと一般的な概念であり、複数の型のデータを扱うことができるコードを指します。継承の場合、それらの型は通常サブクラスです。

その代わりに、Rust はジェネリックを使ってさまざまな可能性のある型を抽象化し、 trait bounds を使ってそれらの型が提供しなければならないものに制約を与えます。これを bounded parametric polymorphism と呼ぶこともあります。

参照

オブジェクト指向経験者のためのRust入門 - Qiita

Object Oriented Programming Features of Rust - The Rust Programming Language