TAKOYAKING’s blog 一覧

TAKOYAKING’s blog

たこ焼き系

Rustでmodはネストできない

ファイルの階層構造 | Rust by Example

modの使い方を練習していたのですが、サンプルではmy/mod.rsにもmodを書いて階層化をしていたので、パスのようにmod my::nestedでnestedファイルのfunctionだけ呼びたい場合、できるかどうか試してみましたが、できませんでした。

やりたいこと

mod my::nested

のように階層化したmodをやりたい

// mod my::nested; // <= 不可
mod my

fn function() {
    println!("called `function()`");
}

fn main() {
    my::nested::function();
}
$ tree .
.
|-- my
|   |-- mod.rs
|   `-- nested.rs
`-- split.rs

結果

できませんでした。

rust - How do 'use' and 'mod' work when there are nested source directories? - Stack Overflow
このStackoverflowに書いてある通り、できないらしいです。

You cannot use :: in a mod declaration.
You need a file src/book/mod.rs, containing:

感想

Rust初心者なので、mod のネストの必要性がそもそもあるかどうかはわかりません・・・

UnityエディタでPREVIEW PACKAGES IN USE が上のバー出る時

f:id:TAKOYAKING:20200130132640p:plain

いつからか気付いた時にはこのような「REVIEW PACKAGES IN USE」が上のバーに出るようになってました。
気持ち悪いので直したいと思います。

環境

Unity2019.2
Mac

注意

Gitのようなバージョン管理していない場合は注意です。

方法

What is "PREVIEW PACKAGES IN USE" - Unity Answers
方法はこれに書いてある通りです。

  1. Window > Package Manager
  2. Reset Packages to defaults
Window > Package Manager

f:id:TAKOYAKING:20200130132646p:plain

するとこんな画面が出てきます。
f:id:TAKOYAKING:20200130132659p:plain

Reset Packages to defaults

があるのでこちらを押します。

すると以下のような警告文が出ます。
f:id:TAKOYAKING:20200130132707p:plain

OKします!

するとPackages/manifest.json
に変更箇所が記載されます(Git管理必須)

PREVIEW PACKAGES IN USE という文字が消えました!
f:id:TAKOYAKING:20200130132714p:plain

備考

UnityAnalitycsも削除されてしまったのでまた入れ直しました。

もうやってしまったあとなのでPreview版と書いてあるものだけを手動で取り除いてもいいような気がします。

感想

なんかスッキリした!

Rust 範囲のシンタックス 「...」VS 「..=」

match | Rust by Example
このサイトでパターンマッチの練習していた時に範囲を表す記法で...を使うと警告が出ました

fn main() {
    let number = 13;

    println!("Tell me about {}", number);
    match number {
        // 単一の値とのマッチをチェック
        1 => println!("One!"),
        // いくつかの値とのマッチをチェック
        2 | 3 | 5 | 7 | 11 => println!("This is a prime"),
        // 特定の範囲の値とのマッチをチェック
        13...19 => println!("A teen"),
        // その他の場合の処理
        _ => println!("Ain't special"),
    }
}
warning: `...` range patterns are deprecated
help: use `..=` for an inclusive range

この例だと13...19は13から19までを表すのですが、13..19と少し違いがわかりづらいです。
13...19は19を含みますが
13..19は19を含みません。

なので19を含む場合は代わりに13..=19と表すことで=がついているので19が含むことが明示できるようになってます。

個人的には..=はとても見やすいなと思います。境界値はいつも含むか含まないか、不安になるのですが、これだと今後不安になることはなくなると思います。

議論されていた記事

`...` vs `..=` for inclusive ranges - ideas (deprecated) - Rust Internals
この記事に書いてありました。

  1. 覚えずらい
  2. We do the opposite of Ruby (because “more dots more numbers” seems strictly better, but still). (Rubyが何とかかんとか...)
  3. タイプミスしやすい
  4. コードが間違っていないかの確認がしやすい

for文での使用例

19を含まない例

for i in 13..19 {
    println!("{}", i);
}

// output
13
14
15
16
17
18

19を含む例

for i in 13..=19 {
    println!("{}", i);
}

// output
13
14
15
16
17
18
19

注意

...は非推奨ですがmatchの時のみ記述できます。
forの時で使用するとエラーが出ます。

fastlaneでiOSビルドをしてiTunesConnectへアップロードする (個人開発)

apps.apple.com

fastlaneを使ってiTunes Connectへmetadataのアップロードを自動化する (個人開発用) - TAKOYAKING’s blog
これの続きで前回はメタデータのみのアップロードだけを行ったので、今回はiOSipaを生成してiTunesConnectへアップロードするというのをやりました

環境

今回やること

  • ビルドしてipaの作成
  • ipaをiTunesConnectへアップロード

metaデータのアップロードとビルドファイルをアップロードの処理を分けた理由

ビルドファイルをアップロードしてもiTunesConnectへ反映されるまで15分くらいあるので、先にビルドファイルだけアップロードして、反映までの間にリリースノートを書いたりしたかったので、分けました。
個人開発なのでこれでいいかなと思いました!

最終的なソースコード

Fastfile

platform :ios do
    desc "Description of what the lane does"
    lane :build do
        # add actions here: https://docs.fastlane.tools/actions
        gym(
            workspace: "/Your/Path/Unity-iPhone.xcworkspace",
        )
    end

    desc "Description of what the lane does"
    lane :release do
        # add actions here: https://docs.fastlane.tools/actions
        deliver(
            submit_for_review: false, # Appleの審査に出すかどうか
            force: true, # If set to true, no HTML report
            skip_screenshots: true, # 時間節約
            skip_binary_upload: false, # ipaアップするかどうか
            copyright: "#{Time.now.year} TAKOYAKING",
            automatic_release: true, # Appleの審査後にこのバージョンを自動的にリリースする
            # metadata_path: "./metadata"
        )
    end
end

after_all do |lane, options|
    slack(
        default_payloads: [],
        message: "#{lane} #{options} All OK!",
    )
end

error do |lane, exception, options|
    slack(
        message: exception.message,
        success: false
    )
end

Appfile

app_identifier ENV["APP_IDENTIFIER"] # The bundle identifier of your app
apple_id ENV["APPLE_ID"]  # Your Apple email address

.env.default

SLACK_URL="https://hooks.slack.com/services/your/path"
APP_IDENTIFIER="com.foo.bar"
APPLE_ID="youremail@example.com"
# FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD=""
SPACESHIP_2FA_SMS_DEFAULT_PHONE_NUMBER="+81 your phone number"

# もし期限切れたら -> fastlane spaceauth -u youremail@example.com
FASTLANE_SESSION=""

これも前回の構成と同じで基本的に大切な設定情報はenvファイルに記述して、コミットしないことにします。

手順

  1. Fastfileを記述する
  2. XcodeのPreferencesのCommand Line Toolsにバージョンを指定する
  3. 設定情報の記述(Appfile, env)と認証
  4. 実行

Fastfileを記述する

今回のlaneは

  • build
  • release

の2種類で
buildはXCodeでビルドしてipaを生成するlaneです。
releaseはiTunesConnectへビルドしたipaをアップロードするビルドです。
今回も成功したらslackへ通知するので、after_allでslackへの記述も行います。

buildのlaneを書くときにはまったこと
Project file is not a project file, must end with .xcodeproj

が出てビルドできなかったです。
Fastlaneにはxcodeproj用とxcworkspace用2つのオプションがあるので、間違えずに、指定してあげる必要があります。

僕のunityではxcworkspaceを使用していたのでworkspaceを指定してあげないといけないです。

gym(
            workspace: "youpath/Unity-iPhone.xcworkspace",
        )

xcodeprojならこちらです。

gym(
            project: "youpath/Unity-iPhone.xcodeproj",
        )

違いを発見するのが難しかったです。

XcodeのPreferencesのCommand Line Toolsにバージョンを指定する

もし指定されているなら大丈夫なのですが、指定されていないとbuildできません。以下のようなメッセージが出るはずです。

Unable to locate Xcode. Please make sure to have Xcode installed on your machine

下のStackOverflowの通り、Xcodeの設定でCommand Line ToolsにXcodeVersionを指定してあげるだけで解決しました!
ios - Unable to locate Xcode. Please make sure to have Xcode installed on your machine - Stack Overflow


設定情報の記述(Appfile, env)と認証

Appfileとenvファイルには前回で行った設定

  • iTunesConnecへの設定情報
  • slackのurl

に加え、認証の設定情報も必要となります。

認証

認証の設定情報がなくてもビルドは可能なのですが、iTunesConnecへアップロードができません。
パスワードを聞かれると思いますが、認証を行わないと何を入れても、NGとなります。

Continuous Integration - fastlane docs

1. Manage your Apple ID
2. パスワード生成
3. FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORDに入れる

以下を参考
fastlaneでAppleIDの2段階認証に詰まったときにやったこと - Qiita

他にもこんなやり方も参考
fastlane/credentials_manager at master · fastlane/fastlane · GitHub

2段階認証の初期値をsmsにする

これはお好みですが、もし初期設定をsmsにしたい場合はSPACESHIP_2FA_SMS_DEFAULT_PHONE_NUMBERを環境変数にセットする
fastlane/Authentication.md at master · fastlane/fastlane · GitHub


PACESHIP_2FA_SMS_DEFAULT_PHONE_NUMBER="+81 090 123 4567"

感想

一応これで前回の記事と合わせて、ビルドして、metaデータとipaをiTunesConnecへアップロードするという流れができました。
個人開発の作業の流れとしては

  • ビルドする
  • ビルドしたのをアップロードする
  • アップロードが反映されるまでにリリースノートを書いてアップロードする
  • 反映されていたら申請

になってます。
使ってみると結構便利です。あとは自動翻訳を残すのみです。

Visual Studio 2019 for Mac Communityでgitの差分が出ない

apps.apple.com

Visual Studio 2019 for Mac Community でgit の差分が出なくて、とても困っています。
普通はソースコードを編集すると差分のある箇所の左側に、緑の色がつくと思います。
f:id:TAKOYAKING:20200114233938p:plain
画像のような感じになるのですが、これは緑色の部分はgitの差分にはなっていませんでした。

現象

修正したら、修正した箇所が緑色になるのですが、やっぱり戻そうとなった時や、コミットした時は緑色は表示されて欲しく無いのに表示されてしまいます。
つまり、gitの差分と対応していないように見受けられます。

人に聞いてみた

slack communityで「改行コードの問題ではないか?」というアドバイスを受けたので改行コードをいじってみたりしたのですが、うまくいきませんでした。

検証

色々操作してみてわかったことがありました。
ファイルを開きなおすと、左の緑色が消えていることからただのローカルの編集履歴なのではないかと思い始めてきました。

git連携できていないという疑念も最初はあったのですが、左下のタブに変更というタブがあり、そこではgitの差分がちゃんと表示されているので、gitが認識されていないということはなさそうです。
f:id:TAKOYAKING:20200114235039p:plain


解決方法

誰か教えてください。
2年くらいずっと困っています。
結構我慢して使っています。

まとめ

ローカルの編集履歴になっているのでこれをgitと連携しないといけないのですが、gitは連携されているようですし、何故なんだろうという気持ちでいっぱいです。
バグなのか、そういう設定があるのか、自分のミスなのか、そういう仕様なのかもはやわかりません。

ただ1年ほど前にtodoと書いても、ピンク色に色がつかないバグがあり、結構気長に待つと、修正されたということもあったので、気長に待つしかないかも知れません。

Rust: i8のキャストと2の補数

型キャスティング | Rust by Example
このサイトで型キャスティングを学びました。
その時 2の補数で型のキャスティングが行われると符号が反転する場合があることを知りましたので、まとめてみます。

現象

// 符号付きの型にキャストする場合、結果は以下の2つを行った場合に等しい
// 1. 対応する符号なしの型にキャストする。
// 2. 2の補数(two's complement)をとる

// 128をu8にキャストすると128となる。128の8ビットにおける補数は -128
println!(" 128 as a i8 is : {}", 128 as i8);

128をi8でキャストすると-128になります。
これはRustが符号付きでは以下のルールでキャストするためです。

符号付きの型にキャストする場合、結果は以下の2つを行った場合に等しい
1. 対応する符号なしの型にキャストする。
2. 2の補数(two's complement)をとる

補数とは

「2の補数」「1の補数」について覚書 - Qiita
このサイトがわかりやすかったです。

  • 足すと桁上がりする数のうち最小の数。 (10進数なら98の補数は2)
  • 足しても桁上がりしない数のうち最大の数。 (10進数なら98の補数は1)

特に2進数は

  • 2の補数(足すと桁上がりする数のうち最小の数。) (ビット反転)
  • 1の補数(足しても桁上がりしない数のうち最大の数。) (ビット反転した数に1を足す)

があります。

128をi8でキャストすると-128になる理由

キャスト (型変換) | サイト構築日記
Rustではないですが、他の言語でも同様の現象があるので、こちらのサイトがわかりやすかったです。

-128になるのをみて、一瞬思ったのが、「128の2の補数は128ではないか」と思いました。
128の2の補数は128で間違い無いのですが、解説を読むとわかりました。

i8の範囲は「-128~127」なので8個のbitを使用しますが、8bit目は符号の役割を果たします。
なので128はi8の範囲からあふれています。
128は2進数では「10000000」となりますが、i8では8個目のビットが立っているとマイナス表現負の数は2の補数で処理されるので-128と表現されてしまいます。
これが、128の2の補数は128ですがi8は-128となってしまう原因です。

感想

補数という言葉を知らなかったので、知れてよかったと思いました。rustだけではなく、他の言語でも同様の現象起こりそうなので、ハマった時に思い出せたら役に立つと思いました。

RustのLinked ListのサンプルでBox<T>の使い方を学習した

apps.apple.com


Linked ListのサンプルでBoxの使い方が出ていて、理解が深まったので備忘録として置いておきます。

コード

テストケース: 連結リスト | Rust by Example
ここより拝借

use List::*;

enum List {
    // Cons: これは、要素をラップし、次の要素へのポインタを保持するタプル。
    Cons(u32, Box<List>),
    // Nil: 連結リストの終端であることを示すノード
    Nil,
}

// 列挙型にはメソッドを付与することができる。
impl List {
    // 空リストの作成。
    fn new() -> List {
        // `Nil` は `List`型を持つ。
        Nil
    }

    // リストを受け取り、その始端に新しい要素を付加したものを返す関数。
    fn prepend(self, elem: u32) -> List {
        // この`Cons` 自体も、その第2要素もどちらもlist型である。
        Cons(elem, Box::new(self))
    }

    // list の長さを返すメソッド
    fn len(&self) -> u32 {
        // このメソッドは、`self`の状態によって振る舞いが
        // 変化するため、matchをする必要がある。
        // `self`の型は`&List`であるので、`*self`は`List`になる。マッチングは
        // リファレンス(`&T`)ではなく実体(`T`)に対して行うのが好ましい。
        match *self {
            // `self`をすでに借用しているので、tailの所有権を取ることができない。
            // 代わりに参照を使用する。
            Cons(_, ref tail) => 1 + tail.len(),
            // 空リストならば長さは0
            Nil => 0
        }
    }

    // Listをheap上の文字列として表したものを返すメソッド。
    fn stringify(&self) -> String {
        match *self {
            Cons(head, ref tail) => {
                // `format!`は`print!`に似ているが、コンソール上に出力
                // する代わりに、heap上の文字列を返す。
                format!("{}, {}", head, tail.stringify())
            },
            Nil => {
                format!("Nil")
            },
        }
    }
}

fn main() {
    // 空の連結リストを作成
    let mut list = List::new();

    // 要素を追加
    list = list.prepend(1);
    list = list.prepend(2);
    list = list.prepend(3);

    // 追加後の状態を表示
    println!("linked list has length: {}", list.len());
    println!("{}", list.stringify());
}

Box

引用

ボックスは、間接参照とヒープメモリ確保だけを提供します。他のスマートポインタ型で目撃するような、 他の特別な能力は何もありません。これらの特別な能力が招くパフォーマンスのオーバーヘッドもないので、 間接参照だけが必要になる唯一の機能であるコンスリストのような場合に有用になり得ます。 より多くのボックスのユースケースは第17章でもお見かけするでしょう。

ユースケースをたくさん見た方が良い気がしてきましたが、ここでは再帰ユースケースを見ました。
Rustの思想ゆえに、こんなシンプルな機能になっているんだろうなと思いました。

なぜBoxを使用するのか?

Box<T>はヒープのデータを指し、既知のサイズである - The Rust Programming Language
解説はここにあって、まとめると

  • コンパイラは事前に使用される領域のサイズを知る必要がある
  • Linked Listは再帰的なデータ構造型になるのでRustはコンパイル時にサイズを知ることはできない
  • 代わりに、Boxというポインタ型にしてしまうことで事前にサイズを計算することを可能にする

なるほどと思ってしまいました。確かにもしデータ構造を以下のように定義すると

enum List {
    Cons(i32, List),
    Nil,
}

無限ループでサイズを計算しないといけないことになってしまうので事前に計算できない。

感想

再帰の使用例しかまだ見ていないので他にも使いどころがきっとあるんだろうと思いながら、Rust勉強しなかったら、こんなこと考えもしなかっただろうなと思いました!
先はまだまだ長い!

本筋とは関係なけど気になったコード

  • len
  • stringify

共に再帰になっています。(再帰の説明はなしです。)

Box::len()メソッドがあり、これを呼び出すにはT::len()を実装してあげないといけないみたいです。なので実態はBox::len()が呼ばれるので、List::len()が呼ばれる仕組みです。
もし実装していないと以下のようなエラーが出ます。

the method `len` exists but the following trait bounds were not satisfied:

個人的メモ

  • refと&の違いがわかっていない。
  • &と*をなぜ使い分けたかわかっていない。