百鬼夜行

素性を知られぬままエンジニアになることを目指します

Rails の chartkick.js のエラーを解決する

f:id:yagyosan:20181206204137p:plain

開発環境構築中に下記のエラーが表示された。

Asset filtered out and will not be served: add Rails.application.config.assets.precompile += %w( chartkick.js ) to config/initializers/assets.rb and restart your server

config/initializers/assets.rbRails.application.config.assets.precompile += %w( chartkick.js ) を書き加えろとのことなので、そのようにした。

そのあと再起動して、解決しました。

ndenvとnode -vのバージョンが合わない時の解決策

ごちゃごちゃしていたのを整理したのでメモっておく。

$ node -v
v11.2.0
$ ndenv versions
  system
* 8.9.4 (set by /Users/yagyosan/project/.node-version)

最初はこの状態。

nodeを参照している場所を変える

ndenvのPATHなどを .bash_profile をすでに書いてある前提で...。

$ which node
/usr/local/bin/node
$ brew uninstall --ignore-dependencies node
$ exec $SHELL -l
$ which node
/Users/yagyosan/.ndenv/shims/node
% node -v
v8.9.4

ついでにnpmも変える

$ which npm
/usr/local/bin/npm
$ npm uninstall -g npm
$ rm -rf /usr/local/lib/node_modules
$ brew prune
$ which npm
/Users/yagyosan/.ndenv/shims/npm

これですべてndenvの支配下になった。

参考

Nodeのバージョン管理をndenvにしたけどうまくバージョンが切り替わらなかったときのメモ - dackdive's blog

「The Book」で Rust 入門 3 後半

前回のあらすじ

yagyosan.hatenablog.com

関数

関数について見ていく

  • エントリーポイント: プログラム実行時に最初に走る関数
  • fn で新しい関数を作ることができる

関数の引数

  • 引数: 関数のシグニチャ?の一部となる特別な変数
  • 正確には仮引数("parameter")と実引数("argument")がある
  • Rusyでは仮引数の型を宣言しなければならない

関数本体は、文と式を含む

  • 関数本体は、文が並び、最後に式か文を置くという形で形成される
  • Rustは式指向性
  • 文: なんらかの動作をして値を返さない命令
  • 式: 結果値に評価されるもの(数値, 数学演算, 関数呼び出しなど)
  • 文はCとRubyのように代入値を返さない
  • 式はブロックの中で文末に;がつかないもの
fn main() {
    let x = 5;

    let y = {
        let x = 3;
        x + 1
    };

    println!("The value of y is: {}", y);
}

戻り値のある関数

  • 関数を呼び出したコードに値を返せる
  • 関数の戻り値は、関数本体ブロックの最後の式の値と同義
  • returnキーワードで「早期リターン」することもできる
  • 関数名() のあとに -> と型を書いて宣言する
  • エラー文に表示される()はタプルを表す
fn five() -> i32 {
    5
}

fn main() {
    let x = five();

    println!("The value of x is: {}", x);
}

コメント

フロー制御

ifとループ制御について

if式

  • if式の条件式と紐付けられる一連のコードは「アーム」と呼ばれることがある(match式のように)
  • 論理値以外を入れるとコンパイルエラーになる
  • else if と組み合わせることもできるが、コードがめちゃくちゃになることもあるのでmatch式が有用
  • ifは式なのでlet文の右辺に持ってくることも可能
  • 変数は単独の型でなければならないため、各アームの結果になる値は同じ型にする

ループでの繰り返し

loopwhilefor がある。

loopでコードを繰り返す

  • 明示的にやめさせるまで永遠に繰り返す
  • break でやめさせることができる

whileで条件付きループ

  • whileキーワードの後に条件を書く
  • loopifelsebreakを組み合わせても同じようなことができる

forでコレクションを覗き見る

  • 添字を使う場合、長さを間違える可能性もある
  • コレクションの各アイテムに対してコードを実行できる
  • インクリメント/ デクリメントしてwhileを使うよりもforRange型を組み合わせル方が良い
fn main() {
    let a = [10, 20, 30, 40, 50];

    for element in a.iter() {
        // 値は{}です
        println!("the value is: {}", element);
    }
}

Range型の例

fn main() {
    for number in (1..4).rev() {
        println!("{}!", number);
    }
    println!("LIFTOFF!!!");
}

とりあえず3章はこれで終わりで、最後のセクションにあったプログラムを別記事でやってみる。

「The Book」で Rust 入門 3 前半

前回のあらすじ

yagyosan.hatenablog.com

一般的なプログラミングの概念

一般的なプログラミング言語の概念の解説と、それがRustの文脈だとどうなるか。

変数と可変性

Rustでは変数は標準で不変だが、可変にする選択肢もある。なぜそうなっているのか。 cargo new --bin variables でプロジェクトを作って確認してみる。

fn main() {
    let x = 5;
    println!("The value of x is: {}", x);
    x = 6;
    println!("The value of x is: {}", x);
}

ビルドして実行する。

error[E0384]: cannot assign twice to immutable variable `x`
 --> src/main.rs:4:5
  |
2 |     let x = 5;
  |         - first assignment to `x`
3 |     println!("The value of x is: {}", x);
4 |     x = 6;
  |     ^^^^^ cannot assign twice to immutable variable

error: aborting due to previous error
  • 不変な変数に2回も代入できないのと、その該当箇所が表示されている
  • 最初に設定した変数が、のちに変更されてしまっているという見つけづらいバグを防ぐことができる
  • mut をつけることで可変にできる(mutable?)
  • トレードオフとして、バグの予防、パフォーマンスと簡潔性(メモリ)
cargo run
   Compiling variables v0.1.0 (file:///Users/yagyosan/develop/rust_study/variables)
    Finished dev [unoptimized + debuginfo] target(s) in 1.40s
     Running `target/debug/variables`
The value of x is: 5
The value of x is: 6

変数と定数(constants)の違い

定数の特徴は以下

  • mutが使えない(常に不変)
  • const キーワードで宣言し、必ず注釈しないといけない -> 次セクションで説明
  • グローバルスコープ含めてどんなスコープでも定義できる
  • 関数呼び出し結果や実行時に評価される値?にはセットできない
  • (すべて大文字かつアンダースコアで単語区切り)

シャドーイング

前に定義した変数と同じ名前の変数を新しく宣言でき、前の変数を覆い隠す(shadow)こと。「シャドーされた」などという。 mutと上書きの違いは以下の通り。

  • letを使わずに再代入をしようとすればコンパイルエラーになる
  • 実行的には新しい変数を生成することになるため、値の型を変えつつ異なる名前を考える必要がなくなる(ex. 文字列型から数値型への変換)
    • 逆にmutを使おうとすると、型が変更されない(=再代入になる)ためコンパイルエラーになる

データ型

Rustの値はすべてデータ型になる。スカラー型と複合型の2種類を見てみる。

  • 静的型付き言語: コンパイル時にすべての変数の型が判明している必要がある
  • コンパイラ: 値と使用方法に基づいて使用したい型を推論し、複数の方が推論される場合は型注釈をつける必要がある

スカラー

単独の値を示す。主に整数、浮動小数点数、論理値、文字の4つ。

  • 整数型
    • i32型がデフォルトのバリアント
    • iが符号付き(=正負を持つ)整数、uが符号なし、数字がビット数
    • 符号付き数値は「2の補数表現」で保持される 符号付きバリアント:-(2<sup>n-1</sup>)以上2<sup>n-1</sup>-1)以下の数値
      • nはビット数
    • 符号なしバリアント: 0以上2<sup>n</sup>-1以下の数値
    • isizeusizeはプログラムが動作しているコンピュータの種類(64ビットアーキテクチャなど)による
    • 整数リテラル(見たままの値ということ)はバイトリテラル以外は_表記ができる
  • 浮動小数点型
  • 論理値型
    • truefalse
  • 文字型

複合型

  • タプル型
    • 複数の型の何らかの値を一つの複合型にまとめ上げるもの
    • (,)で記述する
    • 変数はタプル全体に束縛されているため、値を取り出すインはパターンマッチングが必要
    • letとパターンを使って個別の変数にすることを「分配」と呼ぶ
    • タプルに.と直後にアクセスしたい値の番号をつければ直接タプルの要素にアクセスできる
  • 配列型
    • タプルとは違い、すべての要素が同じ型でなければならない
    • 他の言語と違い、固定長になっている
    • [,]で記述する
    • ベクタ型はサイズの伸縮が可能なので、どちらを使うべきか迷ったらベクタ型を使う
    • 要素にアクセスするには添字を使う[0]
    • 配列の長さを超える添字を使用した場合はコンパイルエラーになる

「The Book」で Rust 入門 2 後半

前回のあらすじ

yagyosan.hatenablog.com

予想と秘密の数字を比較する

ユーザが入力した値と生成した乱数を比較する。

extern crate rand;

use std::io;
use std::cmp::Ordering;
use rand::Rng;

fn main() {
    
    // snip

    println!("your gessed: {}", guess);

    match guess.cmp(&secret_number) {
        Ordering::Less => println!("Too small!"),
        Ordering::Greater => println!("Too big!"),
        Ordering::Equal => println!("You win!"),
    }
}
  • std::cmp::Ordering: Ordering型もenumで、列挙子はLess, Greater, Equal
  • match guess.cmp(&secret_number): cmpメソッドは2値を比較し、match式で返ってきたOrderingの列挙子に合わせて次の動作を決定
    • match: 複数のアームを持ち、各アームのパターンを順番に照合していき、合わないものは無視される

だが上記の式のままではエラーが出る。

cargo run
   Compiling guessing_game v0.1.0 (file:///Users/yagyosan/develop/rust_study/guessing_game)
error[E0308]: mismatched types
  --> src/main.rs:23:21
   |
23 |     match guess.cmp(&secret_number) {
   |                     ^^^^^^^^^^^^^^ expected struct `std::string::String`, found integral variable
   |
   = note: expected type `&std::string::String`
              found type `&{integer}`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.
error: Could not compile `guessing_game`.

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

型の不一致ということなので、下記の行を追加する。

let guess: u32 = guess.trim().parse()
    .expect("Please type a number!");
  • 型推論にも対応しているが、数値型の場合i32がデフォ
  • 文字列と数値型を比較できなかった
  • 新しい値で変数の値を覆い隠す(Shadow)ことが許されるので、型を変えるときはよく使われる
  • trimメソッドが必要なのは、read_lineの入力値が文字列で5\nで改行を含むため
  • u32型は小さな非負整数にぴったり
  • parseメソッドの呼び出しはResult型を返すのでexpectメソッドで扱う

ループで複数回の予想を可能にする

loopキーワードは無限ループを作り出す

extern crate rand;

use std::io;
use std::cmp::Ordering;
use rand::Rng;

fn main() {
    println!("Guess the number!");

    let secret_number = rand::thread_rng().gen_range(1, 101);

    println!("The secret number is: {}", secret_number);

    loop {
        println!("Please input your guess.");

        let mut guess = String::new();

        io::stdin().read_line(&mut guess)
            .expect("Failed to read line");

        let guess: u32 = guess.trim().parse()
            .expect("Please type a number!");

        println!("your gessed: {}", guess);

        match guess.cmp(&secret_number) {
            Ordering::Less => println!("Too small!"),
            Ordering::Greater => println!("Too big!"),
            Ordering::Equal => println!("You win!"),
        }
    }
}

これだと無限ループなので変更していく。また文字入力するとクラッシュするが、その内容は下記。

thread 'main' panicked at 'Please type a number!: ParseIntError { kind: InvalidDigit }', libcore/result.rs:945:5
# mainスレッドでは数値を入力してください
note: Run with `RUST_BACKTRACE=1` for a backtrace.
# 注釈: バックトレースを見るには `RUST_BACKTRACE=1` で走らせて

正しい予想をした後に終了する

breakを追加して、勝った時に終了するようにする

// --snip--
        match guess.cmp(&secret_number) {
            Ordering::Less => println!("Too small!"),
            Ordering::Greater => println!("Too big!"),
            Ordering::Equal => {
                println!("You win!");
        break;
    }
}

main関数の最後にbreakがあるので、これはプログラムを終了することを意味する。

不正な入力を処理する

非数値を入力した時に、クラッシュさせるのではなく数当てを続けられるようにする。

// --snip--
io::stdin().read_line(&mut guess)
    .expect("Failed to read line");

let guess: u32 = match guess.trim().parse() {
    Ok(num) => num,
    Err(_) => continue,
};

println!("You guessed: {}", guess);
// --snip--
  • match式に切り替える
  • _は包括値
  • continueloopの次の段階に移り、再度予想入力を求めるようプログラムに指示する
    • 実質的には、parseメソッドが 遭遇しうる全てのエラーを無視する

最後に秘密の数字を表示している行を削除して完了。

「The Book」で Rust 入門 2 前半

前回のあらすじ

yagyosan.hatenablog.com

数当てゲームをプログラムする

Rustの基礎となる要素について学ぶ。同じようにCargoでプロジェクトを生成した後、まずは自分が入力した数値を出力するプログラムを書く。

予測を処理する

use std::io;

fn main() {
    println!("Guess the number!");

    println!("Please input your guess.");

    let mut guress = String::new();

    io::stdin().read_line(&mut guess)
        .expect("Failed to read line");

    println!("your gessed: {}", guess);
}
  • use std::io; : 標準ライブラリstdのio(入/出力)ライブラリをスコープに導入する
  • デフォルトでは、preludeに存在するいくつかの型のみ使える
    • 使用したい型がpreludeにない場合は、use文で明示的にその型をスコープに導入する必要がある
  • let: 変数の作成(変数は標準でイミュータブル)
  • mut: 可変変数にするための装飾子
  • String::new: Stringを返す関数で、::は関連関数であることを示す
    • 関連関数: 特定のオブジェクトよりも型(この場合はString)に対して実装された関数のことで、静的メソッドと呼ばれる言語もある
  • io::stdin(): std::io::Stdin オブジェクト(=標準入力へのハンドル)を返す関数で、この記述ができるのは行頭でライブラリをスコープに導入したため
  • .read_line(&mut guess): ユーザが標準入力したものすべてを取り出して、文字列に格納する(格納する文字列を引数にとる)
    • io::Result型を返す
    • &mut guess: 格納する時に文字列を変更できるよう可変変数にする
    • &: 引数が参照であることを表し、データを複数回メモリにコピーせずとも、 コードの複数箇所で同じデータにアクセスできる(これもいミュータブル)
  • Result型: 固定された種類の値を持つ型(enum
    • 型のvariant(取りうる値は)がOk列挙子とErr列挙子
  • .expect("Failed to read line");: Err値だった場合プログラムをクラッシュさせ、 引数として渡されたメッセージを表示する
    • 警告を抑制する正しい手段は後で
  • println!("You guessed: {}", guess);: {}は値を所定の場所に保持するもので、一つ以上の値を出力できる

ひとまずこれで出力までできるようになった。

秘密の数字を生成する

ゲームを実行するたびにランダムで秘密の数字が生成されるようにする。

  • クレート: Rustコードのパッケージ、バイナリークレートとライブラリクレートがある
    • バイナリークレート: 実行可能ファイル
    • ライブラリクレート: 他のプログラムで使用するためのコード

まずはCargo.toml[dependencies]randクレートを追加し、ビルドする。

$ cargo build
   Updating registry `https://github.com/rust-lang/crates.io-index`
   Compiling libc v0.2.43                                                       
   Compiling rand v0.4.3
   Compiling rand v0.3.22
   Compiling guessing_game v0.1.0 (file:///Users/yagyosan/Develop/rust_study/guessing_game)
   Finished dev [unoptimized + debuginfo] target(s) in 1m 09s
  • SemVer: バージョンナンバー記述の標準規格
  • Creates.ioにRustのOSSプロジェクトがあり、リポジトリからクレートを引っ張ってこれる
  • Cargo.lock: 明示的にクレートの記述を変更しない限り、 プロジェクトが使用するバージョンを保ってくれる
  • cargo update: クレートのバージョンを更新するが、標準で0.4.0未満のものだけを検索するようになっている

これでクレートが使えるようになったので、ランダムな数値の生成を試みる。

extern crate rand;

use std::io;
use rand::Rng;

fn main() {
    println!("Guess the number!");

    let secret_number = rand::thread_rng().gen_range(1, 101);

    println!("The secret number is: {}", secret_number);

    println!("Please input your guess.");

    let mut guess = String::new();

    io::stdin().read_line(&mut guess)
        .expect("Failed to read line");

    println!("your gessed: {}", guess);
}
  • extern crate rand;: 外部依存として使用することを知らせる行(use randと同じ効果)
  • use rand::Rng;: Rngトレイト(後ほど解説)は乱数生成器が実装するメソッドを定義していて、 このトレイトがスコープにないと、メソッドを使用できない
  • rand::thread_rng(): これから使う特定の乱数生成器を返す
    • .gen_range(1, 101);: 二つの数字を引数間の乱数を生成してくれる
  • cargo doc --open 依存するクレートのマニュアルをビルドしてくれる

続きはまた今度。

「The Book」で Rust 入門 1

前回nannouをインストールしたにもかかわらずろくに触れないので「The Book」を読み込んでみる。2nd Editionです。

まえがき - The Rust Programming Language

yagyosan.hatenablog.com

前書き

  • Rustの根本原理は「エンパワーメント」
  • 難解な低レイヤーのプログラミングがよりやりやすい
  • Rustは低レイヤーだけでなくWebアプリなど他の範囲に適用できる

事始め

インストール

インストール - The Rust Programming Language

$ curl https://sh.rustup.rs -sSf | sh
...
$ export PATH="$HOME/.cargo/bin:$PATH"

インストールして、僕はzsh使っているので ~/.zprofile にPATHを追加。 アップデートとアンインストールは rustup を使う。docも開ける。 rustc --version したら rustc 1.29.1 (b801ae664 2018-09-20) だった。

  • リンカー とは...?
  • フォーラムとIRCもチェックする

Hello, World!

github.com

今回はrust_studyディレクトリを作って、その中でやることにした。 書いたコードはこちらに全て置いてある。

  • 末尾は.rs
  • rustcコンパイルし、実行可能ファイルが生成されるので、ターミナルから実行する
  • fn main() は常にすべての実行可能なRustプログラムで走る最初のコードになり、引数がない
  • rustfmtという自動整形ツールは開発中
  • 4つのスペースでインデント
  • ;を行末につける
  • 簡単なプログラムならrustcで十分だが、肥大化してきたらCargoツールを使う

Hello, Cargo!

cargoを使って新しいプロジェクトを作成できる。

$ cargo new hello_cargo --bin
$ cd hello_cargo
  • --binはバイナリの実行可能ファイルを作る
  • Cargo.tomlファイル、Gitファイル群、main.rsが入ったsrcディレクトリが生成される
  • Cargo.tomlの中身は[package][dependencies]のセクションヘッダーがある
    • [package]: 後の文がパッケージを設定することを示す
    • [dependencies]: プロジェクトの依存を列挙する
    • パッケージのコードはクレート?(多分後で出てくる)として参照される
  • Cargoを使う場合はファイルがsrc内にあることを期待する
  • 最上位ディレクトリはCargoと、READMEなど他のコードに関連しないもの

次にビルドをする。

$ cargo build
$ ./target/debug/hello_cargo
Hello, world!
  • ビルドした時にCargo.locktargetフォルダができる
  • cargo runでビルドと実行を同時にできる
  • runした時、ファイル変更を検知しなかった場合はコンパイルをスキップする
  • cargo checkは実行可能ファイルをつくらずコンパイルできるかをチェックする

OKだったらリリースビルドをする。

$ cargo build --release
   Compiling hello_cargo v0.1.0 (file:///Users/yagyosan/Develop/rust_study/hello_cargo)
    Finished release [optimized] target(s) in 1.57s
  • target/releaseに実行可能ファイルが作成される
  • 再ビルドをかける開発用はdebug、再ビルドせず高速に動くユーザー用はrelease