Borrow<Borrowed: ?Sized>トレイトとToOwnedトレイト

Stringの借用は、&strとして、&strの所有は、Stringとして表すことが出来る。

Rustでは、こういった関係をBorrow<Borrowed: ?Sized>ToOwnedトレイトを使って表すことが出来る。

Note: 参照 - ある所有された値への借用を表す型。コピーセマンティックである。

trait Borrow<Borrowed: ?Sized>

別の型への参照を生成する。

T&Borrowed

BorrowedBorrowする型とよめる

pub trait Borrow<Borrowed>
where
    Borrowed: ?Sized,
{
    fn borrow(&self) -> &Borrowed;
}

f:id:yossan2:20210130161626p:plain

String = Borrow<str>

f:id:yossan2:20210130161825p:plain

impl Borrow<str> for String {
    fn borrow(&self) -> &str {
        &sef[...]
    }
}

Vec<T> = Borrow<[T]>

f:id:yossan2:20210130164100p:plain

Box<T> = Borrow<T>

f:id:yossan2:20210130161919p:plain

Borrowの役割

HashMapにおいて、getメソッドは以下のように定義されている。

impl <K, V, S> HashMap<K, V, S>
where
    K: Eq + Hash,
    S: BuildHasher,

    pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V>
    where
        K: Borrow<Q>,
        Q: Hash + Eq,
  • K: Borrow<Hash + Eq>

これによってキーの型がStringでもBorrow<str>型に型が弱めることができている。

use std::collections::HashMap;
let mut contacts = HashMap::new();
contacts.insert(String::from("Tanaka"), String::from("062-2222-3333"));
contacts.insert(String::from("Yoshida"), String::from("055-1234-5467"));

// &strで値を取り出せる
dbg!(contacts.get("Yoshida"))

trait ToOwned

pub trait ToOwned {
    type Owned = Borrow<Self>;
    fn to_owned(&self) -> Self::Owned;
}

f:id:yossan2:20210130162528p:plain

Cloneトレイトが&TからTへの変換に対して、ToOwnedトレイトは、Tから特定の型(Borrow<T>)への変換。

実装している型

  • str
  • [T]
  • Path

str

impl ToOwned for str {
    type Owned = String; //  Borrow<Self> である必要がある
    fn to_owned(&self) -> String
}

f:id:yossan2:20210130162546p:plain

[T]

impl ToOwned for [T]
where
    T: Clone,
{
    type Owned = Vec<T>;
    fn to_owned(&self) -> Vec<T>
}

f:id:yossan2:20210130162607p:plain

Path

impl ToOwned for Path {
    type Owned = PathBuf;
    fn to_owned(&self) -> PathBuf
}

f:id:yossan2:20210130162618p:plain

PDFファイル解析にあたって

PDFファイル解析にあたって

RustでPDF解析ツールを作成するにあたって、PDFの仕様をまとめる。 (作成プログラムはPDF.jsを参考に実装予定)

PDFとは

PDF32000_2008 1. Introduction より抜粋して意訳

PDFは、ISO 32000によって規定されている電子ドキュメントを実現するためのファイルフォーマットである。

1993年から2007年のISO標準になるまでAdobeによって開発されていたが、Adobe System Version PDF 1.7がISO 32000版の基となっている。 PDFの仕様は後方包括的であるため、PDF1.7はPDF1.0から1.6の仕様をそのまま含んでいる。

PDFのCoreは、PostScriptページ記述言語 から切り離された高い水準の描画モデルであり、テキストやグラフィックの記述をデバイスや解像度から独立して表されている。 それによって紙媒体から電子フォーム、デジタル紙までをカバーすることができる。

PDFは以下の電子ドキュメントに求められる要求を満たしている。

  • バイス、プラットフォーム、ソフトウェアから独立したドキュメントの保全

  • 以下の多様なソースから1つのPDFドキュメントに結合する

  • 正式なものであることを証明する電子署名

  • 作成者に対するセキュリティとパーミッションの許可

  • 障害のある方へのコンテンツの表示性

PDF構文

参照: PDF32000_2008 7. Syntax

PDF構文は、大まかに4つのパートに分けて理解することができる。

  • Objects: 文字列、数字、辞書、配列、ストリームといった基本データ型の定義
  • File構造: オブジェクトの配置やインクリメンタル更新、圧縮、暗号化を実現
  • Document構造: ページやリソースを一つのオブジェクト(indirect object)で表し、オブジェクト階層を実現
  • Content Stream: ページやグラフィックを表すための命令セットを定義

Document構造

参照: PDF32000_2008 7.7 Document Structure

PDFの解析には、まずそれによって得られる成果物であるドキュメント構造がどのようになっているのかを確認する。

PDFのドキュメント構造は、以下のようにドキュメントカタログ辞書をRootとしたオブジェクト階層を表している。 各オブジェクトはindirect objectであり、辞書によって表されている。 (※ indirect object: 相互参照テーブルから直接アクセスすることができる名前付きオブジェクト。)

カタログ辞書

ドキュメントオブジェクト階層のRoot。 トレーラー辞書の\Rootに、このオブジェクトにアクセスするためのオフセット値が記載されている。

辞書には、ページツリーのオブジェクト番号が記載されている。

1 0 obj
    << /Type /Catalog     % カタログ辞書であることを表す
       /Pages 2 0 R       % ページツリーノードのオブジェクト番号
       /Outlins 3 0 R     % アウトラインノードのオブジェクト番号
    >>
endobj

ページツリーノード

各ページのオブジェクト番号が記載されている。

2 0 obj
    << /Type /Pages      % ページツリーノードであることを表す
       /Kids [ 4 0 R     % 各ページのオブジェクト番号が記載
               10 0 R
               24 0 R
             ]
       /Count 3           % ページ数
    >>
endobj
  • /Kids: 各ページのオブジェクト番号が記載されている

ページオブジェクト

リソースが定義されているオブジェクト番号やその中身を表すコンテンツストリームのオブジェクト番号が定義されている。

4 0 obj
    << /Type /Page       % ページオブジェクトであることを表す
       /Parent 2 0 R     % ページツリーのオブジェクト番号
       /MediaBox [0 0 612 792]  % ページの領域を表す
       /Resources << /Font << /F3 7 0 R
                              /F5 9 0 R
                              /F7 11 0 R
                           >>
                      /ProcSet [/PDF]
                  >>
       /Contents 12 0 R   % コンテンツストリームのオブジェクト番号
       /Thumb 14 0 R      % サムネイル
    ...
    >>
endobj

コンテンツストリームオブジェクト

ページを描画するための命令が記述されている。

12 0 obj
<< /Length 65
>>
stream
1. 0. 0. 1. 50. 700. cm    % 現在位置を(50, 700)に移動
BT
    /F0 36. Tf             % 36ポイントの/F0フォントを選択
    (Hello World!) Tj      % 文字列 Hello World! を描画する
ET
ET
endstream
endobj

PDFファイル構造

参照: PDF32000_2008 7.5 File Structure

PDFを解析するにあたっては、PDFのファイル構造を理解する必要がある。 PDFファイル構造は互換性を伴いながら大きく3つ存在するが、それぞれ成果物としてオブジェクト階層を得ることができる。

  1. オブジェクト参照テーブルとトレーラー辞書によるPDF (version 1.0以降) - ランダムにオブジェクトを配置することができ、インクリメンタル更新ができる
  2. 単一オブジェクトストリームによるPDF (version 1.5以降) - PDFの暗号化
  3. Web最適化されたPDF (version 1.2以降) - ページ番号順に関連するオブジェクトが配置される

オブジェクト参照テーブルとトレーラー辞書によるPDF

5つのパートで構成されている

  • ヘッダー
  • バディ
  • 相互参照テーブル
  • トレーラー
  • startxref

例: 相互参照テーブルとトレーラー辞書によるPDF

%PDF-1.0              % ヘッダー
〓〓〓〓〓            % ASCII制御コードが埋め込まれる (バイナリファイルであることをFTPツールなどに判定させる)
1 0 obj               % バディ
    << /Type /Catalog
       /StructTreeRoot 3 0 R
       ...
    >>
endobj

3 0 obj
...
endobj
...

xref                  % 相互参照テーブル
0 7                   
0000000003 65535 f    
0000000017 00000 n    
0000000081 00000 n    
0000000000 00007 f    
0000000331 00000 n    
0000000409 00000 n    
trailler             % トレーラー辞書
    << /Size 100
       /Root 1 0 R 
       /ID ...
    >>
startxref
    2664            % 相互参照テーブルのオフセット位置
%EOF

ヘッダー

PDFバージョンを記載

バディ

カタログ辞書、ページツリーといったオブジェクトを配置

相互参照テーブル

各indirect objectへアクセスするための表が定義されている

例: 相互参照テーブル

xref                  % 相互参照テーブル
0 7                   
0000000003 65535 f    
0000000017 00000 n    
0000000081 00000 n    
0000000000 00007 f    
0000000331 00000 n    
0000000409 00000 n    
xref キーワード

相互参照テーブルの開始を表す

先頭行
0 7

オブジェクト番号0から6のObjectsが定義されていることを表す

2行目以降
nnnnnnnnnn ggggg *n* eol
  • nnnnnnnnnn オブジェクトのオフセット(位置)を先頭からのbytes単位で表す
  • ggggg 5桁の世代番号
  • n in-use項目であることを表す識別子
  • f free項目であることを表す識別子
  • eol 行末を表す2文字。以下の制御文字が使用可
    • SP CR
    • SP LF
    • CR LF
  • 10桁のオフセット(nnnnnnnnnn)と世代番号(gggg)は一文字のSPACEで区切る
  • fee項目になる方法は2つある
    • テーブルの最初の項目 (オブジェクト番号が0で、世代番号が必ず65,535)
    • indirect objectを削除したとき、相互参照テーブルの項目にfreeマークを付ける
      • 世代番号は、次に生成されるオブジェクト番号(現状のオブジェクト番号の最大値をインクリメントする)が与えられる
Examples

EXAMPLE オブジェクト番号3は削除され、その世代番号は次に生成されるオブジェクト番号を表す

xref
0 6                % 0から6つのobjectsを持つ
0000000003 65535 f % object number 0, free
0000000017 00000 n % object number 1, in-use
0000000081 00000 n % object number 2, in-use
0000000000 00007 f % object number 3, free, 最後なのでobject 0にリンクされる
0000000331 00000 n % object number 4, in-use
0000000409 00000 n % object number 5, in-use

EXAMPLE 4つのsubsectionをもつ相互参照テーブル

xref
0 1                  % オブジェクト番号0の一つのエントリーを持つサブセクション
0000000000 65535 f
3 1                  % オブジェクト番号3の一つのエントリーを持つサブセクション
0000025325 00000 n
23 2                 % オブジェクト番号23,24の二つのエントリーを持つサブセクション
0000025518 00002 n
0000025635 00000 n
30 1
0000025777 00000 n
  • 4つのsubsectionから構成され、トータルで5つの項目をもつ
  • オブジェクト番号23は、再使用されている。世代番号が2であるため。

トレーラー辞書

参照: 7.5.5 File Trailer

カタログ辞書の在りかや圧縮、暗号化情報が記載されている

startxref

startxref行は、 トレーラー辞書のブラケット(<<...>>)の次に必ず配置され、%%EOFの間に相互参照テーブルのオフセット位置がバイト値で記載されている。

PDFの解析にあたっては、ファイルの最後から startxref キーワードの値を読み取って、相互参照テーブルにアクセスする。

PDF.jsによる該当コード

src/core/document.js

get startXRef() {


  // Find `startxref` by checking backwards from the end of the file.
  const step = 1024;
  const startXRefLength = STARTXREF_SIGNATURE.length;
  let found = false,
    pos = stream.end; // ポジションを最終行に合わせる

  while (!found && pos > 0) {  // 最終行から1024バイトずつさかのぼってstartxrefを探している
    pos -= step - startXRefLength;
    if (pos < 0) {
      pos = 0;
    }
    stream.pos = pos;
    found = find(stream, STARTXREF_SIGNATURE, step, true);
  }

  if (found) {
    stream.skip(9);
    let ch;
    do {
      ch = stream.getByte();
    } while (isWhiteSpace(ch));
    let str = "";
    while (ch >= /* Space = */ 0x20 && ch <= /* '9' = */ 0x39) {
      str += String.fromCharCode(ch);
      ch = stream.getByte();
    }
    startXRef = parseInt(str, 10);
    if (isNaN(startXRef)) {
      startXRef = 0;
    }
  }
}
return shadow(this, "startXRef", startXRef);

単一オブジェクトストリームによるPDF

参照: 7.5.7 Object Stream

PDF 1.5からstreamオブジェクト内に複数のindirect objectを格納することができるようになった。

これによってファイルそのものを暗号化を実現することができる。

その際、各indirect objectへのアクセスは相互参照テーブルやトレーラー辞書の代わりに、相互参照ストリームが使用される。

2 0 obj             % オブジェクト番号2 (オフセット 3722)
<< /Length ...
   /N 8             % このストリームは8つのObjectsを含む
   /First 47        % ストリームの先頭行までのオフセット
...
>>
stream
 3 0 4 50 5 72      % オブジェクト番号とオフセットが交互に並んでいる
                    % オブジェクト番号3のオフセットは0、4のオフセットは50…
<< ... >>           % オブジェクト番号3
<< ... >>           % オブジェクト番号4
...
endstream

11 0 obj          % Cross-reference stream
<< /Type /XRef    % Cross-reference stream dictionary
   /Index [2 10]  % オブジェうと番号2から11のObjectを含むストリーム
   /Size 100      % トレーラー辞書の**Size**に該当
   /W [1 2 1]     % 相互参照テーブルの各フィールドのサイズをByteで表す。フィールド1が1バイト、フィールド2が2バイト、フィールド3が1バイト
   /Filter /ASCIIHexDecode % 読み取り専用
>>
stream
   01 0E8A 0      % オブジェクト番号2、Type1(非圧縮,オフセット値,世代番号)
   02 0002 00     % オブジェクト番号3、Type2(圧縮, 含まれているオブジェクトストリームのオブジェクト番号, ストリーム内のインデックス)
   02 0002 01     % オブジェクト番号4、Type2(圧縮, 含まれているオブジェクトストリームのオブジェクト番号, ストリーム内のインデックス)
....
   01 1323 0      % オブジェクト番号11、Type1(非圧縮, オフセット値, 世代番号)
endstream
endobj

startxref
byte offset_of_相互参照ストリーム (object 12への)
%%EOF
endobj

相互参照ストリーム

参照: 7.5.8 Cross-Refarence Stream

相互参照テーブルとトレーラー辞書に代わって、オブジェクトストリーム内のオブジェクトにアクセスするための情報を提供する。 streamオブジェクトで定義されている。

相互参照ストリーム辞書の中身
11 0 obj          % Cross-reference stream
<< /Type /XRef    % Cross-reference stream dictionary
   /Index [2 10]  % オブジェうと番号2から11のObjectを含むストリーム
   /Size 100      % トレーラー辞書の**Size**に該当
   /W [1 2 1]     % 相互参照テーブルの各フィールドのサイズをByteで表す。フィールド1が1バイト、フィールド2が2バイト、フィールド3が1バイト
   /Filter /ASCIIHexDecode % 読み取り専用
>>
  • /XRef: このストリームがオブジェクト参照ストリームであることを表す
  • トレーラー辞書の代わりとなる
相互参照ストリームデータの中身

相互参照テーブルが定義されている。

stream
   01 0E8A 0      % オブジェクト番号2、Type1(非圧縮,オフセット値,世代番号)
   02 0002 00     % オブジェクト番号3、Type2(圧縮, 含まれているオブジェクトストリームのオブジェクト番号, ストリーム内のインデックス)
   02 0002 01     % オブジェクト番号4、Type2(圧縮, 含まれているオブジェクトストリームのオブジェクト番号, ストリーム内のインデックス)
....
   01 1323 0      % オブジェクト番号11、Type1(非圧縮, オフセット値, 世代番号)
endstream

相互参照テーブルの1項目は、3つのフィールドで構成されている。

フィールド1 フィールド2 フィールド3
  • フィールド1: 項目のTypeを指定する。Typeは3つ定義されている。(Type0, Type1, Typ2)
  • フィールド2、3は、Typeによってその値が何を示すのか異なる
Type 0
Field Description
1 タイプを指定。00。この項目は相互参照テーブルのfに相当する
2 開放されたオブジェクト番号
3 このオブジェクト番号が再利用される際の世代番号
Type 1
Field Description
1 タイプを指定。01 。この項目は相互参照テーブルのnに相当する
2 オブジェクトへのオフセット値
3 世代番号。値は必ず0
Type 2
Field Description
1 タイプ指定。 02 。このオブジェクトはstream内に定義されたオブジェクトであることを表す
2 このオブジェクトが含まれているストリームオブジェクトのオブジェクト番号
3 このオブジェクトのストリーム内のインデックス

Web最適化されたPDF (直線化PDF)

参照: Annex F Liniearized PDF

Web上に置かれたPDFを表示するビューアーのために、ページ順にページ単位でオブジェクトを配置したPDF。 またヒントテーブルが導入され、各Objectへのアクセスが高速に行えるようになっている。

大きく2つのグループで構成され、1つめは、1ページ目と関連リソースおジェクトまたドキュメントカタログなどドキュメントの構造に関わるObjectがまとめられる。 2つめのグループには、ヒントテーブルと1ページ目以外のObjectsがページ順に配置される。(1ページ目に関連しない、共通リソースはそのあとに配置される)

これによって先頭からファイルを解析できるようになっている。

Web最適化されたPDFかどうかは、ヘッター行のあとに配置されるObjectを読み取ることで判定することができる。

%PDF-1.1        
〓〓〓〓〓
43 0 obj                 % 直線化PDFオブジェクトを表す辞書がヘッダーのあとに配置される
    << /Linearized 1.0   % Version
       /L 54567          % ファイルの大きさ
       /H [475 598]      % ヒントストリームへのオフセット値
       /O 45             % 先頭ページのオブジェクト番号
       /E 5437           % 先頭ページへのオフセット値
       /N 11             % ページ数
       /T 52785          % 相互参照テーブルまでのオフセット値
      ...
    >>
endobj

注意として、Web最適化されたPDFをインクリメンタル更新した場合、再度Web最適化する必要がある。 (ヒントテーブルなどの仕様に関しては、別途まとめる予定)

まとめ

  • PDFは、オブジェクト階層を表す
  • PDFファイル構造は、PDFファイルからオブジェクト階層を生成するための戦略が定義されている
  • オブジェクト階層のノード(indirect object)は、更にプリミティブなオブジェクト(文字列、数字、辞書、配列、ストリーム)によって構成されている
  • PDFをWeb専用に最適化することが出来る

PDFとPostScriptの違い

https://www.adobe.com/jp/print/postscript/pdfs/PLRM.pdf

Introduction

THE POSTSCRIPT® LANGUAGE is a simple interpretive programming language with powerful graphics capabilities. Its primary application is to describe the appearance of text, graphical shapes, and sampled images on printed or displayed pages, according to the Adobe imaging model. A program in this language can communicate a description of a document from a composition system to a printing system or control the appearance of text and graphics on a display. The description is high-level and device-independent.

PostScript言語は、シンプルなインタプリタプログラミング言語であり、パワフルなグラフィック能力を持つ。 その主要なアプリケーションは、Adobe描画モデルに従って、印刷上もしくはディスプレイ上で、テキストやグラフィカルな図形や画像を記述することである。 言語で記述される1つのプログラムは、高レベルかつデバイス独立である。

1.4.4 Portable Document Format (PDF)

Adobe has specified another format, PDF, for portable representation of electronic documents. PDF is documented in the Portable Document Format Reference Manual. PDF and the PostScript language share the same underlying Adobe imaging model. A document can be converted straightforwardly between PDF and the PostScript language; the two representations produce the same output when printed. However, PDF lacks the general-purpose programming language framework of the PostScript language. A PDF document is a static data structure that is designed for efficient random access and includes navigational information suitable for interactive viewing.

Adobeは、ポータブルな電子ドキュメントの表現として、PostScriptとは別のフォーマット(PDF)を規定している。 PDFは、ポータブルドキュメントフォーマット参照マニュアルとしてドキュメントされている。

PDFとPostScript言語は、Adobe描画モデルを元に共有している。 1つのドキュメントは、PDFとPostScript言語で直接変換することが出来る。これは印刷されるとき、2つの表現は同じ出力を生成する。 しかしながら、PDFはPostScript言語の一般的なプログラミング言語フレームワークがかけている。 1つのPDFドキュメントは静的なデータ構造であり、それは効率的なランダムアクセスかつ相互的な関係にたいして最適化されたナビゲーショナルな情報を含んでいる。

まとめ

  • PostScript
  • PDF
    • Adobeの規格
    • ページの記述を表す静的なデータ構造を持ったファイルフォーマット

forループ文

TL;DR

for文で回すには、以下のトレイトのどれかを実装する必要がある。

  • Iterator
  • IntoIterator

配列はforループで回せない

配列は、どちらも実装していないため、forループ文で直接回せない。

let arr = [1,2,3,4,5];
// NG 
for i in arr {
    dbg!(i);
}
/* 
error[E0277]: `[{integer}; 5]` is not an iterator
  --> /Users/yossan/testcodes/rust/garbage/vec_loop.rs:10:14
   |
10 |     for i in arr {
   |              ^^^ borrow the array with `&` or call `.iter()` on it to iterate over it
   |
   = help: the trait `Iterator` is not implemented for `[{integer}; 5]`
   = note: arrays are not iterators, but slices like the following are: `&[1, 2, 3]`
   = note: required by `into_iter`
*/
  • for文ないでは、スライス型への型強制がされない
  • 配列上で、スライス型のメソッドを呼び出すと、スライス型に型強制する

配列の参照はforループで回せる

配列の参照型はIntoIterator<Item=&T>を実装している。

impl<'a, T> IntoIterator for &'a [T]
    type Item = &'a T
    type IntoIter = Iter<'a, T>
for i in &arr { }

Vecはforループで回せる

Vecは、Iterator実装型ではないが、IntoIterator実装型であるため、for文で回すことが出来る。

impl<T, A: Allocator> IntoIterator for Vec<T, A> {
    type Item = T;
    type IntoIter = IntoIter<T, A>;

    fn into_iter(self) -> IntoIter<T, A> {

for文で使うと内部でinto_iterメソッドが呼び出されるため、所有権を失う

let vec = vec![1,2,3,4,5];
for i in vec {
    println!("{}", i);
}
dbg!(vec);
/*
2   |     let vec = vec![1,2,3,4,5];
    |         --- move occurs because `vec` has type `Vec<i32>`, which does not implement the `Copy` trait
3   |     for i in vec {
    |              ---
    |              |
    |              `vec` moved due to this implicit call to `.into_iter()`
    |              help: consider borrowing to avoid moving into the for loop: `&vec`
*/

参照型の方も実装されているので、所有権を失いたくない場合は、参照型の方を使う。

impl<'a, T> IntoIterator for &'a Vec<T>
    type Item = &'a T
    type IntoIter = Iter<'a, T>
let vec = vec![1,2,3,4,5];
for i in &vec {
    dbg!(i);
}

配列型

配列型

サイズ固定配列。[T; N]で表す。 生成には2つの構文がある。

  • [x, y, z]
  • [x; N]: 値xはCopy実装型でなければならない

配列は以下のトレイトを実装する。(ただしTも同様に実装されている必要がある)

  • Copy
  • Clone
  • Debug
  • IntoIterator: &[T; N]もしくは&mut [T; N]に対して実装される
  • PartialEq, PartialOrd, Eq, Ord
  • Hash
  • AsRef, AsMut
  • Borrow, BorrowMut
  • Default: 要素数が32までの配列の場合

配列に対してスライスメソッドを呼び出した場合、スライス型[T]に強制する。 スライスはダイナミックサイズを持ち、また配列には強制しない。

スライスパターンを使うことで、要素を配列外に移動できる。 もし1要素がほしい場合は、mem::replaceを参照。

Examples

let mut array: [i32; 3] = [0; 3]; // 値0で初期化
array[1] = 1;
array[2] = 2;

assert_eq!([1,2], &array[1..]);

// forループ文では、暗黙的にスライス型に変換されない (配列の参照型はIntoIterator実装型)
for x in &arr {
    print!("{} ", x);
}

配列は、Iterator,IntoIterator型どちらも実装していない

// NG
for x in arr {}

スライス型[T]iterメソッドを呼び出すと暗黙的に配列型をスライス型に型強制する。

for x in array.iter() { }

配列の参照型&[T; N]は、IntoIterator実装型。

impl<'a, T, const N: usize> IntoIterator for &'a [T; N]
    type Item = &'a T
    type IntoIter = Iter<'a, T>
for x in &array {}

スライスパターンを使って、配列の外に要素を移動できる。

fn move_away(_: String) {}

let [john, roa] = ["john".to_string(), "Roa".to_string()];
move_away(john);
move_away(roa);

型強制 Unsized coersions

  • 配列型[T;N]は、Unsize<[T]>を実装している
    • Unsize<T: ?Sized>はマーカートレイト
  • CoerrceUnsizedトレイトによって、Unsize<U>を実装しているT型はU型に強制する

これによって、配列において、スライス型のメソッドを呼び出すと、暗黙的に配列をスライス型に強制する。

参照

https://doc.rust-lang.org/std/primitive.array.html

スライスパターン

https://doc.rust-lang.org/reference/patterns.html#slice-patterns

// Fixed size
let arr = [1, 2, 3];
match arr {
    [1, _, _] => "starts with one",
    [a, b, c] => "starts with something else",
};

Iterator::findクロージャーに&&を渡さなければならない理由

https://stackoverflow.com/questions/43828013/why-is-being-used-in-closure-arguments

let a = [1, 2, 3];

assert_eq!(a.iter().find(|&&x| x == 2), Some(&2));
assert_eq!(a.iter().find(|&&x| x == 5), None);

回答

  • a[i32;3]
  • [i32;3]型はiterメソッドが実装されていない
  • [i32;3]型は&[i32]型にdereferenceする ★ 強制型
  • &[i32]型はiterメソッドが実装している
  • iterメソッドは、Iter<i32>型を返す
  • Iter<i32>型は、Iterator<Item=&32>を実装している。Iterator<Item=&i32>実装型
  • Iterator::find<P>(&mut self, predicate: P) -> Option<Self::Item> where P: FnMut(&Self::Item) -> bool
    • Self::Item&i32
    • &Self::Itemとなって綿割れるので&&i32となる
  • i32に戻すには2つのアプローチがある
    • 明示的なdereferenceをする a.iter().find(|x| **x == 2)
    • ダブル参照を破壊するパターンマッチを使う a.iter().find(|&&| x == 2)

パターンマッチによるデリファレンス

let double_ref = &&5;
match double_ref {
    &&val => println!("{} + 3", val),
}

参照

yossan.hatenablog.com

refキーワード

ref キーワード

参照として値を受け取ることが出来る

let ref num = 5; // &i32

パターンマッチ内でも使用することが出来る

let mut triple = (1, 2, 3);
match triple {
    (1, ref mut second, 3) => *second *= 2,
    _ => panic!(),
}
dbg!(triple); // (1, 4, 3)
let mut triple = (1, 2, 3);
match triple {
    // NG
    (1, &mut second, 3) => *second *= 2,
    _ => panic!(),
}
dbg!(triple); // (1, 4, 3)