「労働者が自分の仕事をうまくやりたいなら、まず自分の道具を研ぎ澄まさなければなりません。」 - 孔子、「論語。陸霊公」
表紙 > プログラミング > Go でベイビーステップ

Go でベイビーステップ

2024 年 8 月 23 日に公開
ブラウズ:280

Baby steps with Go

私は、自分のキャリアや興味に役立つ新しい言語を習得する旅の途中で、Go を試してみることにしました。今回は囲碁に挑戦してきました。第一印象としては、とても素敵だと思います。

これはガイド付きツアーではなく、個人的な思い出として私以外の誰かに向けて書かれたものではないでしょう。

私は、 Os-Release-Q という小さなプロジェクトを自分自身に与えました。私の目的は、自分が管理するどのシステムでもバイナリを保持できるようにして、解析したりアイグレップしたりすることなく、必要な情報を正確に出力できるようにすることでした。

最初のハードル: インポート

Web を検索すると、他の人のパッケージのインポートについては多くのことが語られますが、自分自身のコードの編成についてはほとんど書かれていません。ドキュメントでも、懸念事項の分離ではなく、取得に焦点を当てています。

どの言語でも、このハードルにかなりの頻度で遭遇します。それぞれの言語には、どのように対処するか、また、それぞれにどのような制限があるか、課せられているかについて、独自の特異な哲学があるためです。

主に Python のバックグラウンドを持っている私が基礎を学ぶために取り組んだすべての活動の中で、コードを複数のファイルに分割することが、答えを得るまでに最も時間がかかった作業でした。要約すると、次のことがわかりました:

  • トップレベルにはモジュール module-name を宣言する go.mod が必要です
  • その後、トップレベルに src/ ディレクトリを設定し、メイン関数を配置する src/main.go を設定し、パッケージの main 宣言をトップに設定します
  • 他のファイルにコードを入れるのは、パッケージのメイン宣言を含む src/others.go のようなファイルを作成するだけで簡単です。
  • すべての関数と変数は、パッケージ main の他のファイルで直接使用できるようになりますが、ファイルは go build FILES 呼び出しで明示的に指定する必要があります

ローカル サブモジュールの場合、サブモジュールはフォルダー内に存在する必要があります。パッケージ submodule-name .

を宣言できます。

それが src/submod/ にあり、メインの実装者が src/submod/submod.go にあるとします。 main.go では、「module-name/src/submod」をインポートします (go.mod から取得した module-name を使用)。そして、submod.SomeFunction().

を呼び出すことができます。

サブモジュール関数は、名前が大文字で始まる輸入者のみが使用できることに注意してください。したがって、submod.myFunction()は実行できません。submod.MyFunction().

でなければなりません。

サブモジュールとインポートに関しては他にも考慮すべき点があることは確かですが、コードを整理して分離しておくという点では、これが重要です。

物事を正常に保つために、パッケージ main を宣言するファイルを 1 つだけにして、残りをサブモジュールに分離するようにしました。これらは、go build ファイルの FILES リストで宣言する必要がなく、自動的にインポートされます。

基本的なタスクを実行する

Go のこの特殊性を解決した後、残りは非常に簡単に収まりました。すべての基本的なタスクには、もちろん StackOverflow エントリ、または GoByExample.com ページ、そしてより基本的には Go 言語リファレンスがありました。

  • 文字列の処理は文字列パッケージを介して行われます
  • 配列処理には多数のネイティブ関数があり、その中には、base_array = append(base_array, item1, item2) パターンがあります。また、append(base, other_array...) を介して別の配列の値を使用して配列を拡張する場合にも機能します。
  • エラー処理は通常、エラー オブジェクトを渡すことによって行われますが、必ずしもそうとは限りません。
  • 便利な事前設定されたフェーフィングなしのログ用の「log」ライブラリが存在します。これには、エラーをログに記録し、すぐに終了する log.Fatal(message) 呼び出しが含まれています。
  • exec.Command(base, args...) パターンを使用すると、「os/exec」ライブラリ経由でサブプロセスを簡単に呼び出すことができます

特に一般的な 2 つのタスクについては、それぞれの段落に分けて説明します。

エラー処理

基本的なエラー処理は、文字通り制御フローの途中でエラーを処理する必要があり、面倒であるとコメントされることがよくあります。これは、try/catch ワークフローを使用するプログラマにとっては忌まわしいことかもしれませんが、問題が発生する可能性がある時点で問題に対処することは、それほど悪いことではありません。

// explicit return item `err` forces us to be aware of it
// but having the ability to check it in the same breath is not so bad
if result, err := someCall(); err != nil {
    log.Fatal("Sorry.")
}

// Equally valid is
/*
result, err := someCall()
if err != nil {
    log.Fatal("Sorry")
}
*/

fmt.Println(result)

トライ/キャッチ方法を比較

try:
    result = someCall()
    print(result)
except:
    print("Sorry") # a little divorced from potential origin of error
    sys.exit(1)

引数の解析

フラグライブラリの実装がちょっと中途半端な気がしてなりません。現在の形で生き残っていることを考えると、明らかに人々はそれに慣れており、問題なく受け入れています。

program -flag arg1 arg2 を呼び出すと、フラグが実行するように設定されている切り替えが行われ、positionals := flags.Args() が ["arg1", "arg2"]

の配列を返します。

ただし、プログラム arg1 arg2 -flag を呼び出すと、-flags が行うべきことを切り替えません。代わりに、位置指定が ["arg1", "arg2", "-flag"] として与えられます。解析されませんでした。

これは、ls -l が文字通り渡されるプログラム colorize ls -l のようなサブ呼び出しを渡すのに役立つ可能性があります。そのため、ユースケースがわかります。

ただ、世の中のほとんどのプログラムでは、位置項目の前後にフラグ引数を使用できるというだけです。 ls dir1/ -l dir2/ は ls -l dir1/ dir2/ と同じであり、これは Unix および Linux コマンドの大部分に適用される規則です。

これは慣れが必要なだけかもしれませんが、声をかける価値はあります。

Goの目的とユースケース

ファイル インポート パラダイムはさておき、基本的なアプリケーションを実装するのは非常に簡単であることがわかりました。私が間違ったことはすべてかなり明白に感じられ、間違いには意味がありました。本当に「物事をやり遂げる」ことだけに集中できる気がします。

これまでのほんのわずかな使用量から、私の特定のニーズを考慮すると、次のことがわかります

  • 簡単に始められます
  • コンパイル済みバイナリ、ランタイム依存関係なし
  • 型を使用した単純な言語はシェル スクリプトからのステップアップです
  • 簡単なマルチプロセッシングのサポートとされている

オブジェクトや継承の代わりにスパース型を使用すると障害になると思いましたが、今のところは問題ありません。他の言語ではそれらがなくても大丈夫なので、インターフェイスと型を定義するようになると、Lua や bash からのステップアップのように感じるでしょう。願っています。

私がネイティブにコンパイルされる言語を検討したいと思った理由の 1 つは、存在するランタイムの特定のバージョンに依存することなく、簡単に迂回できるバイナリを生成できることでした。

最近、同僚が落胆しながら私のデスクにやって来て、Debian 10 をベースにした古い Node ベース イメージに Java 17 をインストールする問題を解決しようとしていました。 Node のバージョンをアップグレードして新しいベース イメージを取得するか、新しい Debian ベース イメージを使用して Node を手動でインストールして構成するか、インターネットを検索して善意の人がホストするカスタム リポジトリを探して善意の人を探す必要があります。 -ハッキングされた場合、Debian 10 上で動作する Java 17。

デプロイされたソフトウェアにそのような競合するランタイム依存関係がなければ、どれほど簡単になるでしょうか...

運用の観点から、私が感じている大きなメリットは、コードを簡単に記述して ELF バイナリをビルドして「任意のシステム X」にデプロイできるため、競合する必要がないことです。特定のランタイムの正しいバージョンが配置されていることを確認し、競合する依存関係を管理します。

他にも利点があると確信しています。Go でのマルチスレッドとマルチプロセッシングの使いやすさについてはよく言われているので、次のステップとしてそれを探求するミニ プロジェクトを作成するつもりです。おそらく、複数のチャネルで入力をリッスンし、それに応じていくつかの基本的なタスクを実行するものでしょう。以前にいくつかのテスト自動化タスクでその使用例があったため、現時点では私にとって無縁ではありません。

リリースステートメント この記事は次の場所に転載されています: https://dev.to/taikedz/baby-steps-with-go-3ibl?1 侵害がある場合は、[email protected] に連絡して削除してください。
最新のチュートリアル もっと>

免責事項: 提供されるすべてのリソースの一部はインターネットからのものです。お客様の著作権またはその他の権利および利益の侵害がある場合は、詳細な理由を説明し、著作権または権利および利益の証拠を提出して、電子メール [email protected] に送信してください。 できるだけ早く対応させていただきます。

Copyright© 2022 湘ICP备2022001581号-3