占星術殺人事件

著者: 島田荘司 発行日: 1981年

あらすじ:

事件は1936年、ニ・ニ六事件が発生した日、資産家の画家が殺害されるところから始まる。 現場には日記が残されており、6人の娘を生贄にしてアゾートという女神を作り出す計画が書かれてあった。 後日、この記述通りに、娘たちも6体の遺体となって発見されることとなる。

感想:

40年前に書かれたこの小説が御手洗潔の初出となり、舞台は1980年となっている。 事件は44年前であるから、この頃は、まだ戦前は遠い過去ではないかもしれない。

金田一少年の事件簿に出てくるトリックと同一であったため、ここから拝借したのだろうか?

リラ荘殺人事件

著者: 鮎川哲也

1956-57年に雑誌で連載。 著者は、江戸川乱歩から本格派の驍将(ぎょうしょう、強い武将の意)と称されていた。

内容は、 戦後まもない芸術大学の学生達がリラ荘という館を舞台に殺人事件に巻き込まれていく本格ミステリー。

作中に剣持警部が出て来るが、金田一少年の剣持警部はここから名前が取られたのだろうか? ストーリーの展開に古臭さ(とくに警察官の無能っぷりと突然現れた気障な〇〇)を感じるものの、ミステリーは60年も前のものとは思えなかった。

隻眼の少女

単行本発売日: 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