もぐてっく

人は1つ歳をとるたび、1ビットづつ大きくなれると信じてた。

Rustで値を変更してない変数にmutを付けろと怒られてムッとした話

もぐの。齢(undefined)歳にして、Rustに入門しました

なんかLinux拡張機能とかドライバまで書けちゃうと言うことで最近異常な盛り上がりを見せているRust。
衝動的に興味がわいて、勢いで入門してみたもぐのです。

このRustさん。色々特徴はあるんですが特筆すべきはエラーメッセージ。

「こんな書き方させてごめんね。君はこんなコードを書きたかったんだよね?こう書くと良いよ(イケボ)」

って言うのを5行くらいかけて説明してくれるので比較的すらすらとソースが書けます。

そんなこんなで試しにPostgreSQLからデータを取ってくるプログラムを作っていたのですが、 その最中に少々納得のいかない事象に出くわしました。

ざっくり、以下のようなコードです。

use postgres::{Client, NoTls, Error};

fn main() -> Result<(), Error> {
    let client = Client::connect("postgresql://localhost/test", NoTls)?;
    let _rows = client.query("select name from sample", &[])?;

    Ok(())
}

出たエラーはこちら。

error[E0596]: cannot borrow `client` as mutable, as it is not declared as mutable
 --> src/main.rs:5:17
  |
4 |     let client = Client::connect("postgresql://localhost/test", NoTls)?;
  |         ------ help: consider changing this to be mutable: `mut client`
5 |     let _rows = client.query("select name from sample", &[])?;
  |                 ^^^^^^ cannot borrow as mutable

「変数clientがイミュータブルだ。お前が扱ってるPostgres::Clientのインスタンスはミュータブルだから変数に"mut"を付けろこのビ〇グソ野郎(汚い声)」

とのこと。

はぁ?いやいや。見る限り変数clientに入ってるインスタンスに書き込みなんてしてないですやん?
何を根拠にそんなこと言ってるんです???酷くないです??ちょっとー。

気になりだしたら止まらなくなったので、同じような使い方の構造体を用意して色々試してみることにしました。

検証開始!

その1:空っぽの構造体のインスタンスを代入

→成功。そらそうですね。

pub struct Moguno {
}

fn get_moguno() -> Moguno {
    Moguno {}
}

fn main() {
    let _moguno = get_moguno();
}

検証2:フィールドを定義した構造体のインスタンスを代入

→成功。これもそりゃそう。

pub struct Moguno {
    field :i32 ←←←←←←←←←←
}

fn get_moguno() -> Moguno {
    Moguno {field:1}
}

fn main() {
    let moguno = get_moguno();

    println!("{}", moguno.field);
}

検証3:フィールドに値を書き込むコードのある構造体のインスタンスを代入

→OK。へ~。

てっきり「クラス…ゲフンゲフン…構造体の中にフィールドへの代入処理があるかどうか?」が条件だと思ってたのでちょっと意外。

pub struct Moguno {
    field :i32
}

impl Moguno {
    pub fn cool(&mut self) {
        self.field = 100;       ←←←←←←←←←←
    }
}

fn get_moguno() -> Moguno {
    Moguno {field:1}
}

fn main() {
    let moguno = get_moguno();

    println!("{}", moguno.field);
}

検証4:フィールドに値を書き込むメソッドを持つ構造体のインスタンスを代入してから、そのメソッドを呼び出す

→再現。

おおー!ちゃんと書き込みメソッドを呼んでるかまでチェックしてるのか。すごい。

pub struct Moguno {
   field :i32
}

impl Moguno {
   pub fn cool(&mut self) {
       self.field = 100;
   }
}

fn get_moguno() -> Moguno {
   Moguno {field:1}
}

fn main() {
   let moguno = get_moguno();

   moguno.cool(); ←←←←←←←←←←←

   println!("{}", moguno.field);
}

検証5:フィールドに書き込むコードはないが、引数が&mut selfのメソッドを持つ構造体を代入してから、そのメソッドを呼び出す

→再現。

Ok(大体わかった)

pub struct Moguno {
    field :i32
}

impl Moguno {
    pub fn cool(&mut self) {
        // nop ←←←←←←←←←←
    }
}

fn get_moguno() -> Moguno {
    Moguno {field:1}
}

fn main() {
    let moguno = get_moguno();

    moguno.cool(); ←←←←←←←←←←

    println!("{}", moguno.field);
}

結論

つまりこういうことですね。

第一引数が&mut selfなメソッドを呼び出している変数にはmutが必要。

あーすっきりした。

まとめ!

  • 僕の知ってるC++とかで長年の課題だった「変数にconstを付けてもオブジェクトの中身は変更し放題だよね」問題が解決している

  • その解決策が「メソッドの第一引数に自分のオブジェクト取る言語あるやん?あれにmut付いてたらフィールド変更不可にできるんちゃう?」とめっちゃエレガント

  • Rustのこれはあくまで構造体なので?インスタンスを生成するnewメソッドは自分で作らないと存在しない。これで小一時間ハマったりするから注意だ!

gradleマルチプロジェクトのサブプロジェクト全部ひっくるめたjavadocを生成する

gradleマルチプロジェクトのサブプロジェクト全部ひっくるめたjavadocを生成する方法

ググっても決定打がなかったのでまとめ。

Java界隈ってなんかそんなの多い気がする。)

前提は以下。

  • lombokで楽してるソース
  • SpringBootを使ってるソース(XMLは使ってません。全部ソース上で@Autowired)
  • ソースのエンコードUTF-8
  • Visual Studio Code for Windowsのgit bashからgradlewで実行したいです
  • gradleのプロジェクトはフラットレイアウトです
  • リーリエ可愛い!スイレン可愛い!こんな子たちにちやほやされてるサトシ、ズルすぎでしょ!

はっ!雑念が。(ポケモンサンムーン見ながら検証)

成功例

master/build.gradleをこんな感じにします。

plugins {
  // サブプロジェクトにdelombokなソースを生成させるためのプラグイン
  id "io.freefair.lombok" version "6.0.0-m2" apply false

  // 全部入りjavadocを生成するプラグイン
  id "io.freefair.aggregate-javadoc" version "6.0.0-m2"
}

// サブプロジェクトに対するオプション
subprojects {
  // delombokソース生成プラグインを読み込む
  apply plugin: "io.freefair.lombok"

  // javadocの出力文字コードを指定する(これがないとコメントの日本語がWindows-31Jに変換できないと怒られます)
  apply plugin: "java"

  javadoc {
    options.encoding = "UTF-8"
  }
}

// 全部入りjavadocプラグインに対するオプション
aggregateJavadoc {
  // javadocの出力文字コードを指定する(これがないとコメントの文字列がWindows-31Jに変換できないと怒られます)
  options.encoding = "UTF-8"
}

masterディレクトリでaggregateJavadocタスクを実行するとmaster/build/docs/aggregateJavadoc/に全部入りjavadocが生成されてます。

../gradlew aggregateJavadoc

【この記事がお気に召しましたら、ぜひ以下のリツイートをお願いします!】

フラットレイアウトなgradleマルチプロジェクトをJenkinsでビルドする

はじめに

春。成長と発展の季節ですね。

こちらもお仕事で開発してるソフトが複数バージョン並走x複数人開発になって、テスト環境で動いてるのが何なのかさっぱり分からなくなってきたところです。

なので、巷でよく聞くJenkinsくんに各バージョンのブランチをTomcatに自動デプロイしてもらえないかと思いました。

・・・ん?これってテスト環境の最新化以外にもメリットがあるのでは?

例えば、本番環境のリリースするモジュールのルールを

「ver_1ブランチのソースを使うこと(守れよ!絶対守れよ!!)」

って決めるより

「Jenkinsが(ver_1ブランチから)作ったwarファイルを使うこと」

の方が単純明快になるのではないか。

さらに、リリース時にgradlewを叩く必要がなくなるので新入りの学習コストの削減にも寄与できるのでは。

すげぇなJenkinsさん。
これはなんとしても実現しないと。

ハマる

今回自動デプロイする対象はmaster/にsettings.gradleを置くタイプのgradleマルチプロジェクトです。

それをJenkins 2.277.2 + 推奨プラグインで自動化してみると。

設定完了!さてその結果は・・・エラーが出ました。

FAILURE: Build failed with an exception.

* What went wrong:
Project directory '/var/lib/jenkins/workspace/test' is not part of the build defined by settings file '/var/lib/jenkins/workspace/test/master/settings.gradle'. If this is an unrelated build, it must have its own settings file.

アレですね。masterにcdせずにgradlewしたときのアレですね。
どこで設定すんのこれ?

解決策

Invoke Gradle scriptの「高度な設定...」を押すと出てくる「Root Build script」にmasterを設定するとビルド通りました。

f:id:moguno:20210411211949p:plain

f:id:moguno:20210411212032p:plain

【この記事がお気に召しましたら、ぜひ以下のリツイートをお願いします!】