rustでopensslをbuildうまくできないときの対処法

みなさんこんにちは、エルゴヒューマンの椅子をかって腰の調子がすこぶる良い今日この頃です。

さて本題に入りましょう。 現在、統計的機械学習を学ぶためにRustでPRMLに乗っている手法を実装しているのですが、そこではもちろん行列計算や線形代数のツールを使う必要があります。私はrust-ndarrayを利用しています。線形代数を扱うndarray-linalgでは、intel-mklやopenblasなどを用いています。

github.com

そこで私のlinuxのマシンでbuildを行ったところ、

  --- stderr
  thread 'main' panicked at '

  Could not find directory of OpenSSL installation, and this `-sys` crate cannot
  proceed without this knowledge. If OpenSSL is installed and this crate had
  trouble finding it,  you can set the `OPENSSL_DIR` environment variable for the
  compilation process.

  Make sure you also have the development packages of openssl installed.
  For example, `libssl-dev` on Ubuntu or `openssl-devel` on Fedora.

  If you're in a situation where you think the directory *should* be found
  automatically, please open a bug at https://github.com/sfackler/rust-openssl
  and include information about your system as well as this message.

  $HOST = x86_64-unknown-linux-gnu
  $TARGET = x86_64-unknown-linux-gnu
  openssl-sys = 0.9.60

このようなエラーを吐いてとまりました。 そこで、エラーの中身を見てみると、opensslがうまく入っていないようです。

  • OPENSSL_DIRがうまく設定されていない可能性
  • libssl-devが入っていない可能性

があるように感じました。 そこで最初に

openssl version -a

してopensslの状態を調べます。 その結果

OpenSSL 1.1.1f  31 Mar 2020
built on: Mon Apr 20 11:53:50 2020 UTC
platform: debian-amd64
options:  bn(64,64) rc4(16x,int) des(int) blowfish(ptr) 
compiler: gcc -fPIC -pthread -m64 -Wa,--noexecstack -Wall -Wa,--noexecstack -g -O2 -fdebug-prefix-map=/build/openssl-P_ODHM/openssl-1.1.1f=. -fstack-protector-strong -Wformat -Werror=format-security -DOPENSSL_TLS_SECURITY_LEVEL=2 -DOPENSSL_USE_NODELETE -DL_ENDIAN -DOPENSSL_PIC -DOPENSSL_CPUID_OBJ -DOPENSSL_IA32_SSE2 -DOPENSSL_BN_ASM_MONT -DOPENSSL_BN_ASM_MONT5 -DOPENSSL_BN_ASM_GF2m -DSHA1_ASM -DSHA256_ASM -DSHA512_ASM -DKECCAK1600_ASM -DRC4_ASM -DMD5_ASM -DAESNI_ASM -DVPAES_ASM -DGHASH_ASM -DECP_NISTZ256_ASM -DX25519_ASM -DPOLY1305_ASM -DNDEBUG -Wdate-time -D_FORTIFY_SOURCE=2
OPENSSLDIR: "/usr/lib/ssl"
ENGINESDIR: "/usr/lib/x86_64-linux-gnu/engines-1.1"
Seeding source: os-specific

と出てきたので、このパスを~/.zshrcに書き加えました。 でも解決しませんでした。

そこで、libssl-devがうまく入っていない可能性があると思い、

sudo apt-get install libssl

しました。

Reading package lists... Done
Building dependency tree       
Reading state information... Done
E: Unable to locate package libssl-dev

ですが、うまく入らず。

となりうまく入らず... そこでいろいろ調べてみた結果、

/etc/apt/sources.list

に問題がありそうなことがわかりました。

で、sources.listとは何かといいますと、

wiki.debian.org

に詳しく書いてあります。 これを、

askubuntu.com

に従って改変し、

sudo apt update
sudo apt upgrade

したらうまく入りました。

Macで任意のファイルをGoogle Chromeでコマンドラインで開く方法

動機

英語で書かれた論文を読む時DeepLのChrome拡張があると便利。 Ubuntuだとgoogle-chromeで開くことができるが、macだとないらしいので~/.zshrcを編集したらどうにかなったので備忘録として書いておく。

alias chrome="open -a 'Google Chrome'"

これで解決した

dieselが大量のエラーを掃き出してインストールされないときの対処方

問題

dieselのチュートリアルの通りにインストールしようとした。 コマンドは

cargo install diesel_cli --no-default-features --features postgres

これでインストールしようとすると、大量のエラー

error: linking with `cc` failed: exit code: 1
  |
  = note: "cc" "-Wl,--as-needed" "-Wl,-z,noexecstack" "-m64" "-Wl,--eh-frame-hdr" "-L" "/home/hikarukondo/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "/tmp/cargo-installl7MMnA/release/deps/diesel-f9383feb9231cfae.diesel.3yhc16ed-cgu.0.rcgu.o" "/tmp/cargo-installl7MMnA/release/deps/diesel-f9383feb9231cfae.diesel.3yhc16ed-cgu.1.rcgu.o" "/tmp/cargo-installl7MMnA/release/deps/diesel-f9383feb9231cfae.diesel.3yhc16ed-cgu.10.rcgu.o" "/tmp/cargo-installl7MMnA/release/deps/diesel-f9383feb9231cfae.diesel.3yhc16ed-cgu.11.rcgu.o" "/tmp/cargo-installl7MMnA/release/deps/diesel-f9383feb9231cfae.diesel.3yhc16ed-cgu.12.rcgu.o" "/tmp/cargo-installl7MMnA/release/deps/diesel-f9383feb9231cfae.diesel.3yhc16ed-cgu.13.rcgu.o" "/tmp/cargo-installl7MMnA/release/deps/diesel-f9383feb9231cfae.diesel.3yhc16ed-cgu.14.rcgu.o" "/tmp/cargo-installl7MMnA/release/deps/diesel-f9383feb9231cfae.diesel.3yhc16ed-cgu.15.rcgu.o" "/tmp/cargo-installl7MMnA/release/deps/diesel-f9383feb9231cfae.diesel.3yhc16ed-cgu.2.rcgu.o" "/tmp/cargo-installl7MMnA/release/deps/diesel-f9383feb9231cfae.diesel.3yhc16ed-cgu.3.rcgu.o" "/tmp/cargo-installl7MMnA/release/deps/diesel-f9383feb9231cfae.diesel.3yhc16ed-cgu.4.rcgu.o" "/tmp/cargo-installl7MMnA/release/deps/diesel-f9383feb9231cfae.diesel.3yhc16ed-cgu.5.rcgu.o" "/tmp/cargo-installl7MMnA/release/deps/diesel-f9383feb9231cfae.diesel.3yhc16ed-cgu.6.rcgu.o" "/tmp/cargo-installl7MMnA/release/deps/diesel-f9383feb9231cfae.diesel.3yhc16ed-cgu.7.rcgu.o" "/tmp/cargo-installl7MMnA/release/deps/diesel-f9383feb9231cfae.diesel.3yhc16ed-cgu.8.rcgu.o" "/tmp/cargo-installl7MMnA/release/deps/diesel-f9383feb9231cfae.diesel.3yhc16ed-cgu.9.rcgu.o" "-o" "/tmp/cargo-installl7MMnA/release/deps/diesel-f9383feb9231cfae" "/tmp/cargo-installl7MMnA/release/deps/diesel-f9383feb9231cfae.2ljx8vxpfob2gs4q.rcgu.o" "-Wl,--gc-sections" "-pie" "-Wl,-zrelro" "-Wl,-znow" "-Wl,-O1" "-nodefaultlibs" "-L" "/tmp/cargo-installl7MMnA/release/deps" "-L" "/usr/lib/x86_64-linux-gnu" "-L" "/home/hikarukondo/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-Wl,-Bstatic" "/tmp/cargo-installl7MMnA/release/deps/liburl-467eaddfcce53554.rlib" "/tmp/cargo-installl7MMnA/release/deps/libpercent_encoding-6404fa3895139f99.rlib" "/tmp/cargo-installl7MMnA/release/deps/libidna-1cf68800b7ed22b1.rlib" "/tmp/cargo-installl7MMnA/release/deps/libunicode_normalization-65fa459eef99a4b7.rlib" "/tmp/cargo-installl7MMnA/release/deps/libtinyvec-3c50fc017bbed66b.rlib" "/tmp/cargo-installl7MMnA/release/deps/libunicode_bidi-d7adc887995a8f0b.rlib" "/tmp/cargo-installl7MMnA/release/deps/libmatches-389a53a8d07e8373.rlib" "/tmp/cargo-installl7MMnA/release/deps/libtoml-2f55c3bb86f27f38.rlib" "/tmp/cargo-installl7MMnA/release/deps/libtempfile-e18b97729444a80f.rlib" "/tmp/cargo-installl7MMnA/release/deps/librand-2a54687171897a17.rlib" "/tmp/cargo-installl7MMnA/release/deps/librand_chacha-611571fee6109426.rlib" "/tmp/cargo-installl7MMnA/release/deps/libppv_lite86-7acc70b7fe3e6300.rlib" "/tmp/cargo-installl7MMnA/release/deps/librand_core-dd9c19cb313f4f24.rlib" "/tmp/cargo-installl7MMnA/release/deps/libgetrandom-3493a36dcbda61cc.rlib" "/tmp/cargo-installl7MMnA/release/deps/libremove_dir_all-e105efd57f3eec17.rlib" "/tmp/cargo-installl7MMnA/release/deps/libcfg_if-0f89700f04afbfc5.rlib" "/tmp/cargo-installl7MMnA/release/deps/libserde-3465af54848603e5.rlib" "/tmp/cargo-installl7MMnA/release/deps/libmigrations_internals-239d6c6fa4d17fa3.rlib" "/tmp/cargo-installl7MMnA/release/deps/libdotenv-8bb50913892f7b18.rlib" "/tmp/cargo-installl7MMnA/release/deps/libregex-a4a15eaf73f6b450.rlib" "/tmp/cargo-installl7MMnA/release/deps/libutf8_ranges-24dc3098c5a9996e.rlib" "/tmp/cargo-installl7MMnA/release/deps/libregex_syntax-c181b1c5b45966ed.rlib" "/tmp/cargo-installl7MMnA/release/deps/libucd_util-3a31297ed9e71b37.rlib" "/tmp/cargo-installl7MMnA/release/deps/libthread_local-7f22dce31c3b8610.rlib" "/tmp/cargo-installl7MMnA/release/deps/liblazy_static-a2d4e9568ec6e14c.rlib" "/tmp/cargo-installl7MMnA/release/deps/libaho_corasick-1f870e1cc8698ef1.rlib" "/tmp/cargo-installl7MMnA/release/deps/libmemchr-ad18c58549176d99.rlib" "/tmp/cargo-installl7MMnA/release/deps/liberror_chain-07208c47f3f894a2.rlib" "/tmp/cargo-installl7MMnA/release/deps/libbacktrace-4f92aa72f2665e9a.rlib" "/tmp/cargo-installl7MMnA/release/deps/libminiz_oxide-832fc9383d1c709f.rlib" "/tmp/cargo-installl7MMnA/release/deps/libadler-db4c0f56e21a0544.rlib" "/tmp/cargo-installl7MMnA/release/deps/libobject-ff42ba3d842bf54f.rlib" "/tmp/cargo-installl7MMnA/release/deps/libaddr2line-49fa3ae780197da5.rlib" "/tmp/cargo-installl7MMnA/release/deps/libgimli-bdcfc0588434ee82.rlib" "/tmp/cargo-installl7MMnA/release/deps/libcfg_if-6d7e6186e726238c.rlib" "/tmp/cargo-installl7MMnA/release/deps/librustc_demangle-759799f7a70b78ca.rlib" "/tmp/cargo-installl7MMnA/release/deps/libdiesel-deb5637a5f67f223.rlib" "/tmp/cargo-installl7MMnA/release/deps/libpq_sys-86aaf66686c5e02e.rlib" "/tmp/cargo-installl7MMnA/release/deps/libbyteorder-eb338c1472124957.rlib" "/tmp/cargo-installl7MMnA/release/deps/libclap-1df416366c8280d8.rlib" "/tmp/cargo-installl7MMnA/release/deps/libvec_map-405e6da89d886d17.rlib" "/tmp/cargo-installl7MMnA/release/deps/libtextwrap-ab9ff3722de2db3a.rlib" "/tmp/cargo-installl7MMnA/release/deps/libunicode_width-5f4356788e200ee8.rlib" "/tmp/cargo-installl7MMnA/release/deps/libstrsim-4674324dc7d46173.rlib" "/tmp/cargo-installl7MMnA/release/deps/libbitflags-ff7f5482904c4de3.rlib" "/tmp/cargo-installl7MMnA/release/deps/libatty-77e74e8a1493e544.rlib" "/tmp/cargo-installl7MMnA/release/deps/libansi_term-51c4c3bdcd4f8b68.rlib" "/tmp/cargo-installl7MMnA/release/deps/libchrono-dadbce334e241555.rlib" "/tmp/cargo-installl7MMnA/release/deps/libnum_integer-68daeca7f4931930.rlib" "/tmp/cargo-installl7MMnA/release/deps/libnum_traits-57b35a11280e5fc4.rlib" "/tmp/cargo-installl7MMnA/release/deps/libtime-18a87bf33e135542.rlib" "/tmp/cargo-installl7MMnA/release/deps/liblibc-171b7f68884a56a2.rlib" "-Wl,--start-group" "/home/hikarukondo/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libstd-f14aca24435a5414.rlib" "/home/hikarukondo/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libpanic_unwind-48d342a8b48d1d01.rlib" "/home/hikarukondo/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libminiz_oxide-14bc0820888c8eb3.rlib" "/home/hikarukondo/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libadler-9cbd9e217bff06bc.rlib" "/home/hikarukondo/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libobject-31826136df98934e.rlib" "/home/hikarukondo/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libaddr2line-075976a117c8fd5d.rlib" "/home/hikarukondo/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libgimli-2d5cbedfbf17a011.rlib" "/home/hikarukondo/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/librustc_demangle-0474372ff08c5319.rlib" "/home/hikarukondo/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libhashbrown-d437c34460d2315a.rlib" "/home/hikarukondo/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/librustc_std_workspace_alloc-fb61ed1b8cc4de79.rlib" "/home/hikarukondo/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libunwind-bf76d1b643bfc9f0.rlib" "/home/hikarukondo/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcfg_if-a1b53aa7fddcf418.rlib" "/home/hikarukondo/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/liblibc-28585e57fac45c73.rlib" "/home/hikarukondo/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/liballoc-64801769bc15ab28.rlib" "/home/hikarukondo/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/librustc_std_workspace_core-541997b56bb98660.rlib" "/home/hikarukondo/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcore-cdea3c81adab3d12.rlib" "-Wl,--end-group" "/home/hikarukondo/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcompiler_builtins-cd9f15a39fb65cbc.rlib" "-Wl,-Bdynamic" "-lpq" "-ldl" "-lrt" "-lpthread" "-lgcc_s" "-lc" "-lm" "-lrt" "-lpthread" "-lutil" "-ldl" "-lutil"
  = note: /usr/bin/ld: cannot find -lpq
          collect2: error: ld returned 1 exit status
          

error: aborting due to previous error

error: failed to compile `diesel_cli v1.4.1`, intermediate artifacts can be found at `/tmp/cargo-installl7MMnA`

Caused by:
  could not compile `diesel_cli`.

To learn more, run the command again with --verbose.

対処方 githubのissueに記載があった。

sudo apt install libpq-dev

で解決した。

DMPfold

DMPfoldに関しての備忘録です。

概要

  • 距離マップの予測とモデルの生成を2ステップに行うのではなく、イテレーティブに行った。
  • コンタクトマップの予測ではなく、距離マップの予測を行う。
  • Pfam, CASP12, 膜貫通タンパク質に対して従来手法と比較し高精度に予測できた。

結果

f:id:hikarukondo:20200903091919p:plain 上の図はDMPfoldの生成モデルの概要です。 aはCASP12 FMドメインのモデル、bはFILM3のモデル、cはPfamの構造が判明しているもの。dはCASP13FMのターゲットです。

f:id:hikarukondo:20200903092656p:plain にCASP12の主要モデルとの比較を掲載します。 TM-scoreが他のモデルと比較して高いことがわかります。 Rosetta-2000は良いTM-scoreであるが、どのモデルが高いTM-scoreかわからないため使いづらいです。 その一方でDMPfoldは少ないモデルでも高いTM-scoreを出すため、使いやすいモデルであると言えます。

https://media.springernature.com/full/springer-static/image/art%3A10.1038%2Fs41467-019-11994-0/MediaObjects/41467_2019_11994_Fig2_HTML.png?as=webp

また、CASP12の主要な手法とのTM-scoreの分布をaに示します。 この図から分かるように他の手法と比較して、TM-scoreの分布が高い方向になっています。 また、bには、CONFOLD2と比較、cはROSETTAとの比較です。 さらにcはイテレーションの回数によるTM-scoreの変動を表しています。

この図から分かるように、イテレーションを行うことでTM-scoreが上昇しています。

https://www.nature.com/articles/s41467-019-11994-0/figures/3 https://media.springernature.com/full/springer-static/image/art%3A10.1038%2Fs41467-019-11994-0/MediaObjects/41467_2019_11994_Fig3_HTML.png?as=webp

これは膜タンパク質に対しての結果です。 TM-scoreの分布をみると既存手法に比べて高くなっていることがわかります。

https://media.springernature.com/full/springer-static/image/art%3A10.1038%2Fs41467-019-11994-0/MediaObjects/41467_2019_11994_Fig4_HTML.png?as=webp

Pfamに対する結果です。 Pfamの結果既存手法に対しても良い。 ここに関しては完全に理解できなかった。

https://media.springernature.com/full/springer-static/image/art%3A10.1038%2Fs41467-019-11994-0/MediaObjects/41467_2019_11994_Fig6_HTML.png?as=webp

イテレーションによる距離マップの精度の比較。

https://media.springernature.com/full/springer-static/image/art%3A10.1038%2Fs41467-019-11994-0/MediaObjects/41467_2019_11994_Fig5_HTML.png?as=webp

アライメントの質とTM-scoreの関係とタンパク質配列とTM-scoreの関係 配列の長さとTM-scoreには相関がないことがわかる。 また、アライメントが深い方が良い精度予測できる傾向にある。

実験方法

https://media.springernature.com/full/springer-static/image/art%3A10.1038%2Fs41467-019-11994-0/MediaObjects/41467_2019_11994_Fig8_HTML.png?as=webp

DMPfoldの予測の手順。 距離マップ、水素結合、ねじれ角をネットワークを用いて予測し、CNSを用いてモデルを作成する。

https://media.springernature.com/full/springer-static/image/art%3A10.1038%2Fs41467-019-11994-0/MediaObjects/41467_2019_11994_Fig9_HTML.png?as=webp

それぞれを予測する際のネットワークの構造。 aは距離マップを予測するネットワーク。bは水素結合を予測するネットワーク。cはねじれ角を予測するネットワーク。

距離マップは回帰問題では無く、分類問題として解かれる。 距離マップはシンメトリーである必要があるため、そうなるように処理している。

水素結合のネットワークは、横はアクセプター、縦はドナーの尤度を表している。

また、ねじれ角のモデルは、LSTM層を用いることで3次元のテンソルを2次元の行列に変換し予測を行っている。

CNSを用いた予測について。 何もわからn

Rust Tips

Rustlangのtipsに関して書いていきます。 随時更新します。 完全にメモです。俺以外が見ても意味ないです。

現在、rust勉強中です。 まさかり投げないで間違ってたら優しく教えてください。 すぐに訂正いたします。 ご指摘お待ちしてます。

Json

extern crate serde_json;

でインポートして、使い方は、 Value型の変数にfrom_strとかでjson型のデータを保持できる。

extern crate serde_json;
extern crate serde_json::{Result, Value};

use serde_json::{Result, Value};

fn untyped_example() -> Result<()> {
    // Some JSON input data as a &str. Maybe this comes from the user.
    let data = r#"
        {
            "name": "John Doe",
            "age": 43,
            "phones": [
                "+44 1234567",
                "+44 2345678"
            ]
        }"#;

    // Parse the string of data into serde_json::Value.
    let v: Value = serde_json::from_str(data)?;

    // Access parts of the data by indexing with square brackets.
    println!("Please call {} at the number {}", v["name"], v["phones"][0]);

    Ok(())
}

あとは、任意の構造体に対してjsonにパースする方法。

use serde::{Deserialize, Serialize};
use serde_json::Result;

#[derive(Serialize, Deserialize)]
struct Address {
    street: String,
    city: String,
}

fn print_an_address() -> Result<()> {
    // Some data structure.
    let address = Address {
        street: "10 Downing Street".to_owned(),
        city: "London".to_owned(),
    };

    // Serialize it to a JSON string.
    let j = serde_json::to_string(&address)?;

    // Print, write to a file, or send to an HTTP server.
    println!("{}", j);

    Ok(())
}

エラー関係

.unwrap()

doc.rust-lang.org

Option<T>のやつみたいな感じ。 Noneだった時にpanicする。 なんかpanicやたらめったらされたら怖いから普通にmatchで実装した方がいいと思うけど、簡単な実装なら便利そう。

?

doc.rust-lang.org pythonでいうところのtry

before lib.rs

fn read_username_from_file() -> Result<String, io::Error> {
    let f = File::open("username.txt");

    let mut f = match f {
        Ok(file) => file,
        Err(e) => return Err(e),
    };

    let mut s = String::new();

    match f.read_to_string(&mut s) {
        Ok(_) => Ok(s),
        Err(e) => Err(e),
    }
}

after lib.rs

fn read_username_from_file() -> Result<String, io::Error> {
    let mut f = File::open("username.txt")?;
    let mut s = String::new();

    f.read_to_string(&mut s)?;

    Ok(s)
}

こんな感じ。 このコードはstd::fs::read_to_stringで一発で使える。

try!(try!(try!(foo()).bar()).baz())

foo()?.bar()?.baz()?

に省略できる。 エラーならErrreturnしてくれるっぽい。

#[derive(Debug)]

例えば、構造体

struct User {
    name: String,
    ange: u32,
}

このような構造体に対して、中身を出力しようとすると、

let BoKuToTsuZenU = User { name: String::from("BoKuToTsuZenU"), age: 100000};
println!("創造神冒頓単于のスペックワロタwwwwwwww{}", BoKuToTsuZenU);

こうすると、 エラーでます。 解決策は、

#[derive(Debug)]
struct User {
    name: String,
    age: u32
}

fn main() {
    let BoKuToTsuZenU = User {name: String::from("BoKuToTsuZenU"), age: 100000};
    println!("{:?}", BoKuToTsuZenU);
} 

これで動きます。または、{:#?}だとさらに見やすいかもしれない。

構造体のメソッド記法

struct User {
    name: String,
    age: u32
}

impl User {
    fn is_adult(&self) -> bool {
        if self.age > 20 { true }
        else { false}
    }
}

fn main() {
     let god = User{name: String::from("BoKuToTsuZenU"), age: 10000};
     let adult = god.is_adult();
     if adult {
         println!("{} is adult", god.name);
     }
     else {
         println!("{} is not adult", god,name);
     }
}

見たい感じ。詳しいことはここをクリック!!!!

Rustのライフタイムについて

rustにはライフタイムという概念があります。みんなひっかかると思いますが、私も引っかかりました。。。

自分用のメモなのでわかりづらくても悪しからず。

ライフタイム

ライフタイムとは、参照が有効なスコープのことです。 rustではスコープを抜けるとメモリが開放されます。例えば、

変数xyのライフタイムは

fn main() {
    let x = String::from("HogeHoge"); // x ここから
    {
        let y = String::from("fugafuga"); // y ここから
    } // y ここまで
    println!("{}",y);
}// x ここまで

こうなるのは他の言語でもよくあることだと思います。 そこで、rustでは開放されたメモリポインタ(ダングリングポインタ)にアクセスしたり参照できないようになっています。 rustのコンパイラには借用チェッカーが存在しています。 これが、ダングリングポインタを参照しないようにしています。

基本的に参照全てにライフタイムを考える必要があります 例外を最後の方に示します。

借用チェッカー

長い文字列を変換する関数longestに関して、

fn longest(x: &String, y: &String) -> &String {
    if x.len() > y.len() { x }
    else { y }
}

fn main() {
    let x = String::from("BoKuToTsuZenU");
    let y = String::from("Tohoku Univ");

    let longer = longest(x, y);
}

これはエラーになります。

   Compiling rust_test v0.1.0 (/Users/hikarukondo/Documents/rust/rust_test)
error[E0106]: missing lifetime specifier
 --> src/main.rs:1:39
  |
1 | fn longest(x: &String, y: &String) -> &String {
  |               -------     -------     ^ expected named lifetime parameter
  |
  = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `x` or `y`
help: consider introducing a named lifetime parameter
  |
1 | fn longest<'a>(x: &'a String, y: &'a String) -> &'a String {
  |           ^^^^    ^^^^^^^^^^     ^^^^^^^^^^     ^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0106`.
error: could not compile `rust_test`.

To learn more, run the command again with --verbose.

これでエラーになる理由は、戻り値の参照がx,yになるか、コンパイラが判別できないため、戻り値がどちらのライフタイムになるかわからないからです。

ライフタイム引数の記法

この問題を解決するため、参照間の関係を定義するライフタイム引数を用います。 rustのライフタイム引数の定義の仕方は'aみたいな感じで書きます。

ライフタイム引数を持つ関数

同じライフタイム引数では同じライフタイムを持つものとしてコンパイルされます。 longest関数の入力は同じライフタイムを持つものとして同じライフタイム引数を使用します。

fn longest<'a>(x: &'a String, y: &'a String) -> &'a String {
    if x.len() > y.len() { x }
    else { y }
}

fn main() {
    let x = String::from("BoKuToTsuZenU");
    let y = String::from("Tohoku Univ");

    let longer = longest(x, y);
}

こんな感じでライフタイム引数で修飾するとコンパイルできます。 ジェネリックなライフタイム'aは、xyのライフタイムのうち、小さい方に等しい具体的なライフタイムになります。

fn longest<'a>(x: &'a String, y: &'a String) -> &'a String {
    if x.len() > y.len() { x }
    else { y }
}

fn main() {
    let x = String::from("BoKuToTsuZenU");
    let longer;
    {
        let y = String::from("Tohoku Univ");
        longer = longest(x, y);
    }
    println!("{}", longer);
}

このコードはエラーになります。 なぜなら、戻り値のライフタイムは入力のライフタイムの短い方になるからです。 つまり、戻りのライフタイムはyのライフタイムと同じです。 そのため、エラーが起こります。

以降は追記します。

構造体のライフタイム

まずこのコードを見てください。

struct User {
    username: &str,
    email: &str,
    sign_in_count: u64,
    active: bool,
}

fn main() {
    let user1 = User {
        email: "someone@example.com",
        username: "someusername123",
        active: true,
        sign_in_count: 1,
    };
}

これはエラーを吐きます。構造体の要素に参照を持つことも可能ですが、ライフタイムの注釈が必要です。

struct User<'a> {
    username: &'a str,
    email: &'a str,
    sign_in_conunt: u64,
    active:bool
}

で宣言することが可能です。

ライフタイム引数を持つ構造体のメソッド

impl<'a> User<'a> {
    fn somefunc(&self, T){ /* do something */}
}

のように宣言すればできます。

ライフタイムの省略

rustのライフタイムは元々全ての関数で書く必要があったらしいです。 ですが、数多くのコードを解析した結果一意にライフタイムを推定できる場合にはライフタイムの省略することが可能になりました。

ライフタイムの省略には規則があります(参照)。 コンパイラが以下の規則を用いてライフタイムを推定できない参照が存在する場合エラーになります。

  1. 入力に対して:参照である各引数は、独自のライフタイム引数を得る。 fn somefunc<'a>(x: &'a str)や、fn somefunc<'a, 'b>(x: &'a str, y; &'b str)など。 それぞれの引数に対して別々のライフタイムを与えるということだと思います。

  2. 1つだけ入力ライフタイム引数があるなら、そのライフタイムが全ての出力ライフタイム引数に代入される fn somefunc<'a>(x: &'a str) -> &'a strみたいな場合。

  3. 複数の入力ライフタイム引数があるけれども、メソッドなのでそのうちの一つが&self&mut selfだったら、 selfのライフタイムが全出力ライフタイム引数に代入される

例題

fn first_word(s: &str) -> &str {
    let bytes = s.as_bytes();

    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }

    &s[..]
}

上記のコードは動作します。 参照を引数にとり、参照を戻り値にしています。なぜ、コンパイルできるのでしょうか?

私たちはコンパイラ様のお気持ちを考える必要があります。 まず最初の規則参照である各引数は、独自のライフタイム引数を得るを適応します。 つまり、引数は全部独自のライフタイムをうけるとします。 今回は一つしか引数がないのでこの関数のシグニチャは以下のようになります。 fn first_word<'a>(s: &'a str) -> &str{ /* do something */} 次に、二つ目の引数に対してです。 この関数の引数は一つなので、適応できて、fn fiet_word<'a>(s: &'a str) -> &'a str {/* do something */} これにてめでたく、入出力に関してライフタイムは全て推測することができました。

次の例題です。

fn longest(x: &str, y: &str) -> &str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

これに対しても同じように考えましょう。 まず第一の規則を適応してfn longest<'a,'b>(x: &'a str, y: &'a str) -> &str 次にこの関数の引数は二つのため、出力のライフタイムを推測することはできません。 さらに、第三の規則は全く関係ないので、出力の参照のライフタイムを推定することができません。 そのためこのシグニチャではコンパイラ様はライフタイムを推定できないのでエラーです。

静的ライフタイム

'static

で宣言できる。 この宣言の参照はプログラムの全期間を表している。

まとめ

これからもrustのコンパイラとの戦いは続きそうです。 困ったらここを見よう