北海道苫小牧市出身の初老PGが書くブログ

永遠のプログラマを夢見る、苫小牧市出身のおじさんのちらしの裏

emacsで自動改行して欲しくない

特に端末へコピペとかするときに、本当に迷惑なので。

init.el をイジる。まず、 (setq indent-line-function 'insert-tab) なんて記述があったので削除(誰がなんのために入れたんだ?)。

次に、 (electric-indent-mode 0) を追加。

これでほとんどのモードで直ったのだけど、 markdown-mode だけは駄目だったので、ダメ押しの (setq markdown-indent-on-enter nil) を追加。 RET 押下時の indent-and-new-item の呼び出しを抑制できるらしい(勝手にそんなことしないで)。

メジャーモードによって挙動を上書きできるようなので、おかしなメジャーモードがあったら設定などを見直して対応していくしかなさそう。

今日はRustのLT会 Shinjuku.rs #6 @FORCIAの日です

ブログ枠で RustのLT会 Shinjuku.rs #6 @FORCIA に来ています。ブログ枠が何をすればいいのかあまりわかってませんが、とりあえずブログにメモを残しておきます。

開会

  • 会場、懇親会はフォルシア株式会社さんが提供(ありがとうございます!)
  • 次回 11/19 で一周年

Rust初心者がRay Tracer書いてみた / keisukefukudaさん

  • 前回も初心者、相変わらず初心者
  • Ray Tracer は楽しいので、新しい言語を練習するときに良い
  • Ray Tracing とは?
    • 3Dのオブジェクトを平面に描画する
    • ちゃんと計算する
    • オフラインで時間をかけて
    • アルゴリズム、プログラムは以外に簡単。コアのロジックは数行
    • 線形代数は知っている必要がある
  • なぜ Ray Tracing ?
    • 最初のプログラムが複雑だと無理
    • 数値をはけばいいだけ
    • こり始めるといくらでもこれる
    • 絵が出るので楽しい
  • Ray Tracing in One Weekend
    • C++のコードをrustで写経する
    • 3次元ベクトルを定義し、内積などのメソッドを定義
  • ハマりどころ
    • 演算子オーバロード。C++だと簡単
      • std::ops::Add トレイトを実装
      • + だが、参照を取らなければならない
    • 添字演算子
      • std::ops::Index トレイトを実装
      • c++ の const / non-const の実装の分け方がわからない
  • 今後の展望
    • 特殊な Ray Tracer 、 SIMD 化、 WASM で動かす

ネタが無いので盛り上がるプログラムを作った / てくのたのしーさん

  • タイトルを100倍にすると盛り上がる
  • Filmarks さんから映画タイトルをスクレイピング
  • scraper reqwest クレートを利用
  • 8万5千タイトル
  • うち、数字っぽいもの 6500タイトル
    • 漢数字は未対応
  • 第5位「Tokyo 200100/1000/2100 2200:3200-2200:4100」
  • 第4位「貞子300D 200」次元の暴力
    • 次元系は他にたくさんある
  • 第3位「青鬼 ver.200.0」
  • 第2位「裁判長!ここは懲役400年でどうすか」
  • 第1位「永遠の0」 0には何をかけても0でした
  • まとめ
    • 6,000 タイトルに目を通すのは大変
    • ネタがないのにLTに申し込んではいけない
  • 番外編: マイナス2100度、5200Hzのラヴソング
  • Q). このプログラムはどこかに公開されますか
    • A). PR投げつけられそうで怖いが、公開します

最近お仕事で使ったcrateの話 / yukiさん

  • crateとは?
    • rustは標準ライブラリが少なめで、それを補完するもの
  • Rust をどこで使った? → ツール
    • 普段から Scala / Rust / C++ で作る
    • Scalaコンパイル遅い、Macでも動く、小さいと所有権とかではまらない
    • ツールはメンテ不要なので、導入しやすい
  • ansi_term
    • 修飾付きの文字を表示できるクレート
    • 斜体、256色
  • itertools
    • Scala にあるけど rust になかったやつ
    • イテレータに機能を追加するもの
    • chunks 決められた個数ずつに分割する
    • enumerate 添字をつける
    • cartesian_product デカルト
  • scraper reqwest
    • reqwest で HTML を拾って、 scraper で解析
  • chrono
  • 10/26 Rust Tokyo があるので来てね
  • Q). itertools は並列実行は?
    • A). ぱっと見なさそうだったが、あるかも
  • Q). Jst は自分で定義しないと駄目?
    • A). FixedOffset でおーけー
  • Q). 誰かがメンテしているタイムゾーンは?
    • A). なさそう
  • Q). タイムゾーンは相互変換できるから木にしなくていいのでは?
    • A). はい
  • Q). DateTime<Local>DateTime<Jst> の相互変換は?
    • A). 無理
  • Q). クレートはどう探しますか?
    • A). Awesome Rust を探す。更新が1年以内ならO.K.

Rustで10日でwebサービスつくった話 / ryusukefudaさん

  • 開発合宿で、未使用の技術でプロダクトを作っている
    • 去年は Go + Vue.js → プロダクトをリリース
    • 今年は Rust + web component を 4 名で
  • フレームワーク選定
    • actix-web VS rocket
    • HTTP2 のためにactix-web
  • DB選定
    • 使えるもの。MySQL
  • フロントバックエンドの通信
  • 他、 マイグレーションdiesel 、パッケージ管理に cargo
    • 選択肢がない
    • KVSはRedis
    • docker-compose で環境構築
  • 辛かったこと
    • 詰まる。詰まったときに救いがない
    • actix-web の API が苦しい。型指定など
    • actix と actix-web の棲み分けが不明
    • 通信処理。 future を使ったが、 tokio
    • Ruby/Goなら瞬殺できる実装を3hかかって性格が歪んだり
  • actix-cors actix-session actix-redis などを利用
  • 良かったこと
  • フロントの担当者は、WYSIWYG部分を作っていた
  • 予告: 次回はデプロイ編
    • デプロイに 50 分かかっている。ビルドが時間かかりすぎ
    • Cargo.toml をいじると時間がかかる
  • Q). 10日でfutureまで理解できるもの??
    • A). 人間にはちょっと早い。コンパイルを通す作戦に出た。途中から理解を辞めた
  • Q). デプロイ時間がGoだと?
    • A). 5分か10分。Rustはキャッシュ使っても35分。安いプランなので。
      • ローカルの Docker for Mac--release ビルドで6分くらい

What is std::sync::atomicについて / matsu7874

  • シングルスレッドではデータの更新は問題がない
  • マルチスレッドでは、書いている途中に読まれると、何が読まれるのか不明
  • 不可分にする、または、操作する数を 1 つにする
  • Atomic とは?
    • 書き込み途中に読まれない、読み込み途中に書かれない
  • std::sync::atomic Atomic型を提供する
    • Sync トレイトが実装されており、スレッド間共有が可能
    • Arc でラップして使う
  • Atomic 以外の方法
    • Mutex、 RWLock
    • 速度は "perormace comparison of mutex rwlock and atomic types in rust" というスライドを参照
    • Atomic が爆速
  • AtomicUsize 同士の比較をしたい
    • std::cmp::PartialEq トレイトを impl する必要がある
    • 自分で定義したトレイトまたは型しか impl できない
  • newtype パターンを使えば良い
    • struct Bar(Foo)
  • まとめ
    • スレッドでデータ共有するのであれば、専用の型が必要
    • Atomic が高速
    • new type パターンは便利

WebSocketで人生アガります / 宇宙ツイッタラーXさん

  • 最終的にはビットコインで人生がアガる予定(ライブコーディング)
  • main 関数を作る
  • bitFlyer API に rust サンプルがあるが、コンパイル通らない
  • struct Client
  • ws クレートの Handler トレイトを実装
  • on_open on_message を実装
  • connect 関数を利用。 Client 型で Sender 型を受け取って利用
  • 取引情報が欲しいので、 bord というチャネルを使う
  • on_open"lightning_board_BTC_JPY" チャネルを "subscribe" するメッセージを send する
  • on_messageMessage::Text を受け取った場合のみ定義する
  • 「皆さん、日常的にデシリアライズくらい毎日やってるわって感じでしょう」
  • serde::DeserializeJSON をパース
    • JSONからコードを自動生成するサイトがあるらしい
  • 変なJSONも来ていてエラー
    • 「怒りの unwrap をしていたけど辞めましょう」
    • シリアライズに成功したときのみ表示 → 成功
  • まとめ
    • リアルタイム取引は早いのがいい、 C++ は無理でしょう、 rust は適している

rustのcombineで入力されたトークンによる場合分け

combine を使ったときに、例えば識別子のパース時に予約語を弾く方法。

最初は and_then を使うのかと思ったのだけど、 エラーの型指定がえらく面倒 だったので諦め。というか、なんか絶対違うだろうと思って別の方法を模索。

Parser のドキュメントに以下の例がある。これだ。

digit()
    .then(|d| {
        if d == '9' {
            value(9).left()
        }
        else {
            unexpected_any(d).message("Not a nine").right()
        }
    })

ということで、 then を使うと Parser を返す処理を書けるので、ここで unexpected_any を使って失敗させてやればよい。

ちょっとこのコードを見てぎょっとするのは left()right() だが、これは if で分岐するパーサの戻り値が違うため。 rust ではよくみるやつだが、 combine のパーサコンビネータParser トレイトを持つメソッドごとの独自の型 Value<I, T>Unexpected<I, T> を返す。ということで Either にまとめれば型を混ぜて1つにできてハッピーなのだが、次に、どっちを left にしてどっちを right にするのがいいのかという疑問が出てくる。

正解は実はどちらでもよくて、これはエラーを表す Either ではないので、 right が正常処理というような意味はない。どちらでもいいから、入っているパーサーを実行するだけである。実際、 EitherParser トレイトの実装を見ると以下のようになっている。

impl<L, R> Parser for Either<L, R>
where
    L: Parser,
    R: Parser<Input = L::Input, Output = L::Output>,
{
    ..snip..
    fn parse_lazy(&mut self, input: &mut Self::Input) -> ConsumedResult<Self::Output, Self::Input> {
        match *self {
            Either::Left(ref mut x) => x.parse_lazy(input),
            Either::Right(ref mut x) => x.parse_lazy(input),
        }
    }
    ..snip..
}

R: Parser<Input = L::Input, Output = L::Output> の制約は、2つのパーサの入出力が揃っていないとまずいことを表す。

rust の publicsuffix は遅過ぎる

rust で Public Suffix List を扱うクレートの速度が遅過ぎるので、パッチを書いた。最右部のラベル ( .jp .com など) でルールを区分けして、後は愚直に線形探索という方法だったので、そりゃあ遅すぎるよねって感じ。

github.com

実は PerlDomain::PublicSuffix でも速度の問題にあたって書き直したことがあるのだけど、このときはドメインのバリデーションが重過ぎるのが原因で、 このコミット で解消されている 1 。 rust の publicsuffix でも バリデーション は比較的重いので、この点も改善できるのかもしれない。

ところで、今回の PR で一箇所気に入らないのが この部分 。ツリーを構築しながら潜るのに current 変数を子のノードを指すよう更新したいのだけど、子のノードを作るために可変参照が必要なので別の変数 cur に所有権を移してからじゃないと更新できない。ここをもうちょっとスマートに書く方法はないんだろうか。


  1. このコミットを自分で作ったわけではなくて、自分たちでスクラッチから書いたバージョンではバリデーションを外すなどした、という話。

今日は Roppongi.rs #1 の日です

職場から近いので Roppoingi.rs に来ました。自分のためにメモを残しておきます。ハッシュタグ#roppongirs

Rustがいかにエンジニアの脳を楽にさせているかをC++初心者が語る〜関数篇〜 / @natsu_no_yuki さん

  • C++歴15年の初心者
    • rvalue reference, template特殊化などが使えるのは初心者らしい
    • rust歴は3日
  • プログラミングの歴史からrustの良さを考える
  • C言語: メモリ管理を何もしてくれない
    • プログラマが明示的にメモリを確保したり・・・ (C、C++のことです)
  • C++ と rust の比較
  • C++で関数呼び出しをすると、コピーされてコンストラクタが二回呼ばれる例
    • スタックにローカル変数、戻り先アドレス、引数を積む
    • 引数を積むために2回インスタンスが作られるので、無駄
    • よって、参照渡しをする
  • C++で関数の戻り値で二度コンストラクタが呼ばれる例
    • (処理系によるが)EAXレジスタへコピーする。よってコンストラクタが二回呼ばれる
    • 戻り値を参照にする → 開放されたメモリを参照してしまう
    • スタックに実体があるが、関数から戻るときに破棄されてしまう
    • コンパイルが通ってしまう。 C++ では気が付きにくいバグ
    • ヒープにデータを作る (演算子 new で作る) → 問題ない
    • bad_alloc exception メモリ不足。開放しなければならない ( delete )
  • delete をどうするか
    • 同じレイヤで開放するのが鉄則
    • destroyedXxx のような名前の関数を用意する
    • C++ を書いているひとは、この問題とずっと戦っている
  • rust の場合は、普通に返すだけで所有権が移るので問題ない
    • 脳が嬉しい → Rust いいね

なぜBlockchainはRustを選ぶのか / @jkcomment さん

  • Blockchain の話は多いけど、細かい話はしない
  • rust について
  • なぜ rust の知名度が低いのか
    • OSSプロジェクト、サービスが少ない
    • goは k8s とか。 rust の場合は、 Rust が一番
    • システム系はC言語一強
    • 学習コストが高い、難しそう
  • Blockchainのプロジェクトでは、rustの採用率が高い
  • Blockchainとは?
    • P2Pで動作する分散型大腸システム。ブロックを一定時間ごとに生成してチェーンに。ハッシュで暗号化
    • bitcoin, Polkadot, libra
    • C++: bitcoin, EOS, zilliqa / Go: Ethereum Klaytn Quorum
    • rust: Ethereum(Parity), Polkadot(Substrate) ..他多数
  • なぜrust?
    • 特に理由はない
    • blockchain そのものは、言語はなんでもよい
    • rust のプロジェクトも、理由は明確にしてない
  • それでも理由を考えてみると
      1. 性能
      1. Blockchain に必要なモジュールが用意されている
      2. Networking(P2P), Cryptography, Consensus, Storage などに関する処理が必要
  • P2P (Peer to Peer)
    • 対等なやり取り。オーバレイネットワーク。実現するために必要な機能が多い
    • Transport, Discovery, Peer Routing, NAT超え
    • libp2p
      • すべてのPJが採用しているわけでもないが、多くのPJで採用
      • もともとrustが一番いい実装だったが、今はgo
  • Cryptography / 791 crateも登録されている
  • wasm / WebAssembly
    • VMではなくwasmを使う流れになってきている
    • go/C# はランタイムが大きすぎ、 C/C++ はめんどう
    • rust は楽でバイナリも小さい。安全。
  • Substrate
    • Parity社が開発した Blockchain Framework (wordpress的なもの)
    • 簡単に独自の Blockchain が作れる
    • Substrate ベースのプロジェクトは増えている (zero-chain, Plasm, Edgeware, shasper, ChainX ...)
  • rust の悪い使い方
    • Substrate ... マクロ使い過ぎ、トレイト地獄、 match 地獄
    • マクロは展開後のコードでエラーが起きるのでわかりにくい
  • rustの使いみち
    • OSが作れる
    • Webアプリケーションも作れる (Rocket, Iron, actix-web)
    • フロントエンド開発 feat wasm
  • rustがいいと思う理由
    • (個人的に) OOPが好きじゃない
    • 速さ、堅牢さは正義
  • rustで苦労した点
  • まとめ
    • ものづくりは楽しむべき。rustで楽しむ、Blockchainも楽しい

(LT) rustで実装されたLibra Move言語とは

  • Libra : FB の金融プラットフォーム。2019年6月
    • BFTモデルの Libra Blockchain が基盤
    • 組んでいるメンバーが強い
  • Move : Libra 上でスマートコントラクトを書く言語
    • 安全とセキュリティを重視
  • バイトコードを利用 : bytecode verifier でチェックするので安全
  • アセットをオープンシステムに乗せるの難しさ
    • Scarcity / 生み出せない、複製できない
    • Access control / 所有者のみがコントロールできる
  • block chain言語の問題
    • 資産をInteger で表現、型がない、エラーハンドリング
    • Scarcityの責任が開発者に
    • Access control が柔軟じゃない
  • Move での解決策
    • first-class assets: integer 以外を利用、コピーと履きを明示 ( copy/move )
    • flexibility: modules/resources/proceture (クラス、オブジェクト、メソッド)
    • safety : bytecode verifier
    • verifiability
      • 動的ディスパッチの禁止(コールグラフがたんじゅん、解析がしやすい)
      • mutability の制限 (value の変更はrefernce 経由のみ。所有権システムを利用)
      • モジュール化
  • 開発のエコシステムはまだこれから
    • Libra Blockchain へデプロイはできない
    • ドキュメントや開発ツールがない。
    • Collection や Generics は今はない

Strassen の行列積を求めるアルゴリズム

アルゴリズムイントロダクションで、行列積を求める奇妙なアルゴリズムを見つけたので rust で実装した。簡単のため、行列の表現には ndarray を用いている。

#[macro_use]
extern crate ndarray;
use ndarray::prelude::*;
type Matrix = Array2<i32>;
type MatrixView<'a> = ArrayView2<'a, i32>;
type MatrixViewMut<'a> = ArrayViewMut2<'a, i32>;

fn strassen_multiply_inner (a: MatrixView, b: MatrixView, mut c: MatrixViewMut) {
    let n = a.rows();

    if n == 1 {
        c[[0, 0]] += a[[0, 0]] * b[[0, 0]];
        return;
    }

    assert_eq!(n & 1, 0);
    let m = n / 2;

    let a11 = a.slice(s![..m, ..m]);
    let a12 = a.slice(s![..m, m..]);
    let a21 = a.slice(s![m.., ..m]);
    let a22 = a.slice(s![m.., m..]);
    let b11 = b.slice(s![..m, ..m]);
    let b12 = b.slice(s![..m, m..]);
    let b21 = b.slice(s![m.., ..m]);
    let b22 = b.slice(s![m.., m..]);

    let s1 = &b12 - &b22;
    let s2 = &a11 + &a12;
    let s3 = &a21 + &a22;
    let s4 = &b21 - &b11;
    let s5 = &a11 + &a22;
    let s6 = &b11 + &b22;
    let s7 = &a12 - &a22;
    let s8 = &b21 + &b22;
    let s9 = &a11 - &a21;
    let s10 = &b11 + &b12;

    let mut p1 = Matrix::zeros((m, m));
    strassen_multiply_inner(a11, MatrixView::from(&s1), MatrixViewMut::from(&mut p1));

    let mut p2 = Matrix::zeros((m, m));
    strassen_multiply_inner(MatrixView::from(&s2), b22, MatrixViewMut::from(&mut p2));

    let mut p3 = Matrix::zeros((m, m));
    strassen_multiply_inner(MatrixView::from(&s3), b11, MatrixViewMut::from(&mut p3));

    let mut p4 = Matrix::zeros((m, m));
    strassen_multiply_inner(a22, MatrixView::from(&s4), MatrixViewMut::from(&mut p4));

    let mut p5 = Matrix::zeros((m, m));
    strassen_multiply_inner(MatrixView::from(&s5), MatrixView::from(&s6), MatrixViewMut::from(&mut p5));

    let mut p6 = Matrix::zeros((m, m));
    strassen_multiply_inner(MatrixView::from(&s7), MatrixView::from(&s8), MatrixViewMut::from(&mut p6));

    let mut p7 = Matrix::zeros((m, m));
    strassen_multiply_inner(MatrixView::from(&s9), MatrixView::from(&s10), MatrixViewMut::from(&mut p7));

    {
        let mut c11 = c.slice_mut(s![..m, ..m]);
        c11 += &(&p5 + &p4 - &p2 + &p6);
    }
    {
        let mut c12 = c.slice_mut(s![..m, m..]);
        c12 += &(&p1 + &p2);
    }
    {
        let mut c21 = c.slice_mut(s![m.., ..m]);
        c21 += &(&p3 + &p4);
    }
    {
        let mut c22 = c.slice_mut(s![m.., m..]);
        c22 += &(&p5 + &p1 - &p3 - &p7);
    }
}

fn strassen_multiply (x: &Matrix, y: &Matrix) -> Matrix {
    assert_eq!(x.rows(), x.cols());
    assert_eq!(y.rows(), y.cols());
    assert_eq!(x.cols(), y.rows());

    let mut result = Matrix::zeros((x.rows(), y.cols()));

    let x_view = MatrixView::from(x);
    let y_view = MatrixView::from(y);
    let result_view = MatrixViewMut::from(&mut result);

    strassen_multiply_inner(x_view, y_view, result_view);

    result
}

書籍にも自明ではないと書かれている通り、なんとも煩雑で意味不明な計算である。直感的にはキャッシュをうまく使うことで、本来8回再帰しなければならないところを7回に済ませていることで、計算量が n^ {log _ 2 8} のところを n^ {log _ 2 7} で済ませるということになるだろう。

このアルゴリズムは本に書いてあった手順を愚直に実装したのだが、嫌な点がある。 Matrix::zeros や行列の和 &b12 - &b22 などはメモリを確保して行列を作って返す関数なので、この実装だと再帰する度にメモリを確保してしまう。

これを何とかする方法を探していたら、 stackoverflow で C 言語のプログラムが紹介されている のを見つけた。直感的には出力と同じサイズの行列のメモリを確保しておけばいい気はしつつ、再帰しながら使うことを考えると空き領域は計画的に使う必要がある。

fn strassen_multiply_inner2 (a: MatrixView, b: MatrixView, mut c: MatrixViewMut, mut d: MatrixViewMut) {
    assert_eq!(a.rows(), a.cols());
    assert_eq!(b.rows(), b.cols());
    assert_eq!(a.cols(), b.rows());

    let n = a.rows();

    if n == 1 {
        c[[0, 0]] = a[[0, 0]] * b[[0, 0]];
        return;
    }

    assert_eq!(n & 1, 0);
    let m = n / 2;

    let a11 = a.slice(s![..m, ..m]);
    let a12 = a.slice(s![..m, m..]);
    let a21 = a.slice(s![m.., ..m]);
    let a22 = a.slice(s![m.., m..]);
    let b11 = b.slice(s![..m, ..m]);
    let b12 = b.slice(s![..m, m..]);
    let b21 = b.slice(s![m.., ..m]);
    let b22 = b.slice(s![m.., m..]);

    let (c1, c2) = c.split_at(Axis(0), m);
    let (mut c11, mut c12) = c1.split_at(Axis(1), m);
    let (mut c21, mut c22) = c2.split_at(Axis(1), m);

    let (d1, d2) = d.split_at(Axis(0), m);
    let (mut d11, mut d12) = d1.split_at(Axis(1), m);
    let (mut d21, mut d22) = d2.split_at(Axis(1), m);

    d11.assign(&a12);
    d11 -= &a22;
    d12.assign(&b21);
    d12 += &b22;
    strassen_multiply_inner2(d11.view(), d12.view(), c11.view_mut(), d21.view_mut());

    d11.assign(&a21);
    d11 -= &a11;
    d12.assign(&b11);
    d12 += &b12;
    strassen_multiply_inner2(d11.view(), d12.view(), c22.view_mut(), d21.view_mut());

    d11.assign(&a11);
    d11 += &a12;
    strassen_multiply_inner2(d11.view(), b22.view(), c12.view_mut(), d12.view_mut());

    c11 -= &c12;
    d11.assign(&b21);
    d11 -= &b11;
    strassen_multiply_inner2(a22.view(), d11.view(), c21.view_mut(), d12.view_mut());

    c11 += &c21;
    d11.assign(&b12);
    d11 -= &b22;
    strassen_multiply_inner2(a11.view(), d11.view(), d12.view_mut(), d21.view_mut());

    c12 += &d12;
    c22 += &d12;
    d11.assign(&a21);
    d11 += &a22;
    strassen_multiply_inner2(d11.view(), b11.view(), d12.view_mut(), d21.view_mut());

    c21 += &d12;
    c22 -= &d12;
    d11.assign(&a11);
    d11 += &a22;
    d12.assign(&b11);
    d12 += &b22;
    strassen_multiply_inner2(d11.view(), d12.view(), d21.view_mut(), d22.view_mut());

    c11 += &d21;
    c22 += &d21;
}

fn strassen_multiply2 (x: &Matrix, y: &Matrix) -> Matrix {
    assert_eq!(x.rows(), x.cols());
    assert_eq!(y.rows(), y.cols());
    assert_eq!(x.cols(), y.rows());

    let mut result = Matrix::zeros((x.rows(), y.cols()));
    let mut tmp = Matrix::zeros((x.rows(), y.cols()));

    strassen_multiply_inner2(x.view(), y.view(), result.view_mut(), tmp.view_mut());

    result
}

d を一時領域として使うのだが、再帰の度に d11 d12 d21 d22 の4つの領域を順に使っている。最大で必要な一時領域の数は 3 つ(最後に現れる strassen_multiply_inner2(&MatrixView::from(&d11), &MatrixView::from(&d12), &mut d21, &mut d22);再帰)なので、これでうまく実装できる。しかし、これまた最初の数式から違っていて、なんで行列の積が求められているのか意味不明である。 そして、実装の際に所有権と MatrixView(|Mut) の使い方でハマったので、 MatrixView(|Mut)::from&mut をそこらにつけて誤魔化している。もしかすると、もっと簡潔な記述法はあるかもしれない。 viewview_mut で良さそうだったので書き換えておいた。

rustのsliceとlen()

こう書くと、

fn main() {
    let mut a = [1, 2, 3, 4, 5];
    let x = &mut a[3..a.len()];
    println!("{:?}", x);
}

こう怒られる。

error[E0502]: cannot borrow `a` as immutable because it is also borrowed as mutable
 --> src/main.rs:3:23
  |
3 |     let x = &mut a[3..a.len()];
  |                  -----^-------
  |                  |    |
  |                  |    immutable borrow occurs here
  |                  mutable borrow occurs here
  |                  mutable borrow later used here

こうなら良い。

fn main() {
    let mut a = [1, 2, 3, 4, 5];
    let n = a.len();
    let x = &mut a[3..n];
    println!("{:?}", x);
}

3..a.len() を評価してから a の可変参照を取るならなんの問題もないような気もするのだが、実際このコードは、

use std::ops::IndexMut;

fn main() {
    let mut a = [1, 2, 3, 4, 5];
    let x = a.index_mut(3..a.len());
    println!("{:?}", x);
}

と同値であり、 &mut aa.len()fn index_mut<'a>(&'a mut self, index: Side) -> &'a mut Self::Output の引数となる。第1引数で可変参照を渡そうとしているのに第2引数で不変参照を用いているので怒られる。

error[E0502]: cannot borrow `a` as immutable because it is also borrowed as mutable
 --> src/main.rs:5:28
  |
5 |     let x = a.index_mut(3..a.len());
  |             - ---------    ^ immutable borrow occurs here
  |             | |
  |             | mutable borrow later used by call
  |             mutable borrow occurs here

まあそもそも、このサンプルのように末尾まで取りたいのなら、以下のように書くのが正しい。

fn main() {
    let mut a = [1, 2, 3, 4, 5];
    let x = &mut a[3..];
    println!("{:?}", x);
}

a.len() - 1 まで取りたいのなら分けて書かないと駄目なのかな。