interfaceの実装(@Golang)

Golangのinterfaceの実装はimplementsのように明示することなく、interfaceで定義したメソッドを全て実装すればinterfaceとしての性質を持ちます。それゆえDependency Injectionも分離したコードが書けるわけですが、他の言語から入ると戸惑いがあるのも事実。

以下の簡単な例で実装時の違いを見てみます。以下のコードではcase 1 ~ 3のいずれかの行を有効にすればコンパイルはできますが、意味はそれぞれ違います。

case 1 : 最も一般的な記述方法、StatStructがinterfaceを実装と認識するのでmain()からinterfaceのメソッドが使えます。

case 2 : 構造体に要素を持たせた場合、この場合はcase 1と同じですが、要素を使わないなら空の構造体にしとくのが自然。

case 3 : このケースとcase 1/2との違いはこの行を記述するだけでStat()の実装がなくともinterfaceを実装したことになること。コンパイルはできますが実行時にStat()が呼べないのでエラーになります。実はこれはDIの手法そのものですが。

package main 

import "fmt"

type Stater interface {
    Stat()
}

//type StatStruct struct {}     // case 1
//type StatStruct struct {a int}    // case 2
type StatStruct struct {Stater}     // case 3

func (StatStruct) Stat() {
    fmt.Println("It's fine today")
}

func main() {
    st := StatStruct{}
    st.Stat() 
}

現実的にはメソッドの実装ではcase 1を使うことが普通だと思います。さらに言えば構造体無しでもStat()を定義すれば動きますが、それはおそらくGoのデザインパターン(ideom)から外れるように思います。

 

admin

DI(Dependency Injection) — (@Golang)

DIはコードをinterfaceを介することでソースコード間の依存性を少なくして、例えばDBの接続種類の入れ替えとかテストの時にスタブに切り替えるとかをコードの修正を最低限で実行できる方法です。結果としてコードの保守性が向上するということになります。

https://free-engineer.life/golang-dependency-injection/

の記事がわかりやすかったのでこれを事例にしてみます。

実際にはVScodeで動かしてみて、パッケージが全てmainでは気持ちが悪いのでパッケージ名は分離しています。以下パッケージ分離版のコードです。

https://github.com/chateight/golang/tree/master/basic/di

<task_usecase(use case.go)のDI実現のキーコード部分>

// Saveメソッドの実装はTaskRepositoryで行っている
type TaskRepositoryInterface interface {
	Save(Task) (Task, error)
}
// structのフィールドにTaskRepositoryInterfaceを持たせる
// こうすることで、CreateTaskメソッドでDBへの保存処理を呼ぶことができる
type TaskUsecase struct {
	repo TaskRepositoryInterface
}

~~途中省略~~

func (usecase TaskUsecase) CreateTask(title string) (task.Task, error) {
	t := task.Task{Title: title}
	task, err := usecase.repo.Save(t)
	return task, err
}

つまりinterfaceを経由した抽象的な呼び出しにより依存先をinterfaceとすることで、CreateTask中ではinterfaceを呼び出し、結果として実装(ここではsave())と分離がされているので、例えば他の実装との入れ替えが簡単にできるということになります。

Golangの場合には明示的にinterfaceをimplementで明示しなくとも、interfaceで定義した関数のフットプリントと同じ関数が実装されていればimplementされたことになるのでDIの記述のようなケースでも、他の例えばJavaなどの比較すると綺麗にコードが分離できます。Golangはクラシックなオブジェクト指向では無いですが、現代的な抽象化手段を持つ言語です。

 

admin

toioをtoio.jsで動かしてみる

toioを動かすにはいくつかの手段がありますが、最も簡単なのはiPadのアプリをインストールして使うScratchの拡張機能としてtoioが使えるtoio Doを使うこと。toio Doはブラウザ経由でも動きますが、まだベータ版なので保存機能とかが無いようでiPadアプリを使うのが一番確実。

さらには、Node.js用のライブラリにtoio.jsというのがあって、

https://github.com/toio/toio.js

このGitHubにはサンプルプログラムもあって簡単に動作確認できます。動作環境はmacbook Air apple silicon M1/Ventura 13です。

上記リンクにサンプルプログラムの動かし方があるので、そのままやってみました。

いくつかサンプルありますが、動画で動かしているのは、

% yarn example:keyboard-control

になります。

async function main() {
  // start a scanner to find nearest cube
  const cube = await new NearestScanner().start()

  // connect to the cube
  await cube.connect()

上のコードはサンプルコードからの切り出しですが、cubeへの接続は専用の機能NearestScanner()が用意されて、電源の入っている一番近くのcudeに接続されるようになっています。package.jsonにある”@toio/scanner”: “^1.0.0″にNearestScanner()も含まれていることになります。

 

admin

micro:bitもtoioもScratch Linkで接続できると言うことは、

技術情報が公開されていて色々弄れそうなのでtoioを購入、まだ手元には届いていませんが、toioをScratchから使う時にはパソコンとの接続にはScratch Linkを使う(Web Bluetooth API)と言う記述がありました。したがってmicro:bitもコマンドが公開されてるだろうと思って調べてみると、

https://qiita.com/youtoy/items/c98c0996458a21fc1e67

と言う事例がありました。2021年の記事なのでmicro:bit V1向けかどうかは微妙なのでV2では多少変更が必要(少なくともUUIDは別物になる)かもしれませんが。まあ元々ScratchやMakeCodeはどちらもブラウザ上で動作して、ブラウザとmicro:bit間の通信手段はScratch Linkなので中を通るデータは変わってもtoioでも使えるのでしょう。

上記のサンプルはパソコン側での受信なので、パソコンからの送信は、

https://qiita.com/yagshi/items/abfb3717fb978d031b7e

があります。

いずれにしてもbluetoothとのブラウザからの接続はWeb Bluetooth APIを使いますが、ブラウザが限定でChromeがメジャーブラウザです。

過去記事:https://isehara-3lv.sakura.ne.jp/blog/2021/04/01/microbitでbluetooth通信/

C/C++で低レベルでの接続には、

https://tech.microbit.org/software/runtime/

が関連ドキュメントになりそうですがmicro:bit V2はCODALというハードウェアの抽象化手段があるようです。

 

admin

Rustのテキスト

Rustの場合、コミュニティがしっかりしていてドキュメントの整備もきちんとされているので、特別にテキストを購入する必要はなさそうです。

https://doc.rust-jp.rs/book-ja/title-page.html

の15章あたりまで、ダラダラと読んでますが、例は

https://doc.rust-jp.rs/rust-by-example-ja/

こちらを使えば、一通りの理解はできるように思います。

結局のところ、Rustの仕様のかなりの部分はメモリ管理をコンパイラが判断できるようにユーザーが記述するコードで指示することに集約されるだろうと思う。したがって堅牢性と実行速度を要求するOSやアプリケーションにはRustを使うと言う選択になるんだろう。組み込みやIoTなどで小規模な開発では、コードが冗長になりがちなので、Rustである必要性はあるのかと言う感覚はあります。

 

admin

クロージャー(Golang and Rust)

現代の言語ではクロージャー機能を多くの言語で持っていますが、同じ機能(単純な1の加算)をGolangとRustで実装の比較。Rustの説明で出てくる、クロージャーは環境をキャプチャーできる匿名関数という定義はわかりやすい。

<Golang>

package main

import "fmt"

func add(i int) func() int {
	n := i

	clo := func() int {
		n++
		return n
	}

	return clo		// equivalent to specify anonymous function here and maybe it's more popular
}

func main() {
	a := add(1)
	for i := 1; i < 10; i++ {
		fmt.Println(a())
	}
}

<Rust>

fn f1(i: i32) -> Box<dyn FnMut -> i32> {	// FnMut recieves &mut self
	let mut i = i;						// dyn keyword sepcifies a trait pointer in the heap area
	Box::new(move || {				// force to allocate values in the heap area
		i += 1;
		i
	})
 }
 
 fn main() {
	 let mut cup = f1(0);
	 for _ in 1..10 {
		 println!("{}", cup());
	 }
 }

コードの参考は、

https://scrapbox.io/takker/Rustで関数を返す関数を作

FnMutの解説は、

https://qiita.com/hiratasa/items/c1735dc4c7c78b0b55e9

Fnでは変更できないし、FnOnceでは一度しか呼べないのでFnMutの指定になります。

比較してみると、Rustの方が細かな指定が必要ですが、これはメモリ管理に関する指定を明示的に行わなければいけない言語だからでしょう。したがってこれぐらいの例では変わらないけれども、コードは長めになります。比較してGolangは細かな操作はしなくとも使うだけなら簡単ということになるでしょうか。

Box<dyn … >については、

Boxは変数をスタック領域ではなくヒープ領域への割り当て、dynについては以下の通り、

https://doc.rust-lang.org/stable/rust-by-example/trait/dyn.html

“Rust tries to be as explicit as possible whenever it allocates memory on the heap. So if your function returns a pointer-to-trait-on-heap in this way, you need to write the return type with the dynkeyword

 

admin

 

構造体

GolangやRustでは積極的に構造体を使い、また構造体を効率的に使うための言語仕様も用意されていますが、その背景は必要なデータセットは構造体にまとめることで、読みやすく従ってバグも入りづらいコードを書くことにあるだろうと思います。

同じ機能をGolangとRustで記述してみます。

<Golang>

type Rectangle struct {
  length  float64
  breadth float64
}

func (r Rectangle) area() float64 {
  return r.length * r.breadth
}

<Rust>

struct Rectangle {
    length: f64,
    breadt: f64,
  }
impl  Rectangle{
  fn area(&self) -> f64 {
    return &self.length * &self.breadt
  }
}

Rust呼び出し方、

  fn main() {
    let mut rec = Rectangle{length:0.0,breadt:0.0};

    rec.length = 20.0;
    rec.breadt = 30.0;

    print!("{}", rec.area())
}

Golangでは

(r Rectangle)

を使って構造体と結びつけ、Rustでは

impl Rectangle

でインターフェースの如くimplementでメソッドを定義していますが、上記二種の結果は全く同じ出力をするし、記法にも大差ありません。c++ではこのような記法はないので、現代の言語の特徴と言ってもいいのではないかと思います。

 

admin

関数(メソッド)ごとに文字列と文字列スライスは意識しないといけない(@Rust)

Rustの文字型にはString(可変長)、文字列スライス(固定長)、char型(1バイト)の三種類がありますが、関数やメソッドによって引数や適用が変わってきます。

以下は文字列を全て小文字に変換する関数to_lowercase()の例ですが、操作対象は文字列スライスでなければ機能しませんし、出力はString型になります。

VScodeだと、型は自動で補完してくれますね。

fn main() {
    let text = "heLLO worLd";
    let result: String = text.to_lowercase();
    let search= "worl";

    if result.contains(search){
        println!("{} and {}", result, search);
    }

以下は関数(メソッド)のAPIの記述です、

何故、操作対象は文字列スライス型でなければならないかですが、おそらく変更がなくて参照するだけだろうからだと思います。処理量の観点でも参照の方が少なくて済むだろうし。

 

admin

 

VScodeでラズパイPico Wの環境セットアップ

Thonny、Arduino IDEで環境作ってみて、おそらく現状主要な3つの選択肢だろうと思う最後の選択肢になりますが、VScodeでPlatformIO使ってみます。

以下のリンクでPico Wも含めたセットアップ手順が記載されているのでほぼそのままです。

https://logikara.blog/raspi-pico-init/

PIO homeで新しいプロジェクトを設定して、ボードにPicoを選択後にplatformiio.iniにPico Wの設定を書き込みます。

platformio.iniファイルが変更されるとその内容に従って、必要なモジュールの読み込みが開始されてPlatformIOの領域にクローンが開始され、しばらく待つと完了。

後は通例通り、srcディレクトリのmain.cppにLチカコードを入力して、Pico Wが外部ファイルに見える状態で転送、転送完了するとPico Wが切り離されてプログラムが動作開始します。実は外部ファイルモードにしていなくとも、VScodeが外部ファイルモードにしてUSBシリアルでファイル転送をして、再度外部ファイルモードを終了するという一連の操作を自動で行なってくれるのでこれは便利です。

特にArduino言語で差し障りなければ、VScode + PlatformIOが一番素直であるように思います。

 

admin

ラズパイPico WのWi-Fi接続機能

MicroPythonのサンプルあったのでそのままですがMicroPythonでつないでみた。

構成はラズパイPico Wの既存のWi-Fiに接続、同じネットワークのブラウザから制御というArduinoやM5Stackなどと同じ方法です、APモードにも出来るでしょうが。

http://usicolog.nomaki.jp/engineering/raspberryPi/raspberryPi_picoW.html

のコードそのまま、ただしWi-FiのSSID/PWは自分用に設定必要です。

Thonnyのコンソールメッセージ、自分に割り当てられてIPアドレスを表示しています。

指定の通りブラウザから呼び出すと、ブラウザの画面はこんな感じ。

ON/OFF指定でオンボードLEDの状態が切り替わります。

 

admin