Go1.4のリリースノート(日本語訳)

JavaScriptを有効にしてください

前書き

Go 1.4 のリリースノートをChatGPTで日本語に翻訳しました。
Go 1.4 Release Notes

次のバージョン:Go1.5のリリースノート(日本語訳)
前のバージョン:Go1.3のリリースノート(日本語訳)
リリースノート一覧:Goリリース情報

Go 1.4の紹介

最新のGoリリース、バージョン1.4は、1.3から6ヶ月後に予定通り登場しました。言語の変更はわずかで、後方互換性のあるfor-rangeループの簡単なバリアントと、ポインタへのポインタのメソッドに関するコンパイラの変更が含まれています。

このリリースは主に実装作業に焦点を当てており、ガベージコレクタの改善と、次のいくつかのリリースで完全に並行するコレクタを展開するための準備を行っています。スタックは現在連続しており、必要に応じて再割り当てされるため、新しい「セグメント」をリンクすることはありません。このリリースにより、悪名高い「ホットスタックスプリット」問題が解消されます。新しいツールもいくつか利用可能で、goコマンドにビルド時のソースコード生成のサポートが追加されています。また、AndroidとNative Client(NaCl)でのARMプロセッサのサポートと、Plan 9でのAMD64のサポートも追加されています。いつものように、Go 1.4は互換性の約束 を守り、ほとんどすべてのものが1.4に移行しても変更なしにコンパイルおよび実行を続けることができます。

言語の変更

For-rangeループ

Go 1.3まで、for-rangeループには2つの形式がありました。

go
1
2
3
for i, v := range x {
    ...
}

および

go
1
2
3
for i := range x {
    ...
}

ループの値に興味がない場合でも、変数(おそらくブランク識別子 )を指定する必要がありました。例えば、for _ = range xのように。なぜなら、形式for range x { ... }は文法的に許可されていなかったからです。この状況は不自然に思えたため、Go 1.4からは変数なしの形式が合法になりました。このパターンはまれにしか発生しませんが、発生した場合にはコードがよりクリーンになります。更新: この変更は既存のGoプログラムに対して厳密に後方互換性がありますが、Goの解析ツリーを分析するツールは、この新しい形式を受け入れるように変更する必要があるかもしれません。なぜなら、RangeStmtKeyフィールドがnilになる可能性があるからです。

**Tへのメソッド呼び出し

以下の宣言を考えてみましょう。

go
1
2
3
type T int
func (T) M() {}
var x **T

gcgccgoの両方がメソッド呼び出しx.M()を受け入れていましたが、これはポインタへのポインタxの二重参照です。Goの仕様では自動的に単一の参照を挿入することが許可されていますが、二重参照は許可されていないため、この呼び出しは言語定義に従って誤りです。したがって、Go 1.4ではこれが許可されなくなり、これは破壊的な変更ですが、影響を受けるプログラムは非常に少ないでしょう。更新: 古い誤った動作に依存するコードはもはやコンパイルされませんが、明示的な参照を追加することで簡単に修正できます。

サポートされるオペレーティングシステムとアーキテクチャの変更

Android

Go 1.4は、Androidオペレーティングシステムで動作するARMプロセッサ用のバイナリをビルドできます。また、mobile サブリポジトリのサポートパッケージを使用して、Androidアプリケーションによってロードされる.soライブラリをビルドすることもできます。この実験的なポートの計画についての簡単な説明はこちら で入手できます。

ARM上のNaCl

前回のリリースでは、32ビットx86(GOARCH=386)および32ビットポインタを使用する64ビットx86(GOARCH=amd64p32)のNative Client(NaCl)サポートが導入されました。1.4リリースでは、ARM(GOARCH=arm)のNaClサポートが追加されました。

AMD64上のPlan9

このリリースでは、カーネルがnsecシステムコールをサポートし、4Kページを使用している場合に、AMD64プロセッサ上のPlan 9オペレーティングシステムのサポートが追加されました。

互換性ガイドラインの変更

unsafe パッケージを使用すると、データの実装やマシン表現の内部詳細を利用してGoの型システムを破ることができます。互換性に関して、Go互換性ガイドライン でunsafeの使用が何を意味するかは明示的に指定されていませんでした。もちろん、unsafeなことを行うコードに対して互換性の約束をすることはできません。この状況はリリースに含まれるドキュメントで明確にされました。Go互換性ガイドラインunsafe パッケージのドキュメントは、unsafeコードが互換性を維持する保証がないことを明示しています。更新: 技術的には何も変わっていません。これはドキュメントの明確化に過ぎません。

実装とツールの変更

ランタイムの変更

Go 1.4以前では、ランタイム(ガベージコレクタ、並行性サポート、インターフェース管理、マップ、スライス、文字列など)は主にCで書かれており、一部のアセンブラサポートがありました。

1.4では、多くのコードがGoに翻訳され、ランタイム内のプログラムのスタックをガベージコレクタがスキャンし、どの変数がアクティブであるかについて正確な情報を得ることができるようになりました。この変更は大きなものでしたが、プログラムに対して意味的な影響はありません。この書き換えにより、1.4のガベージコレクタは完全に正確になり、プログラム内のすべてのアクティブなポインタの位置を認識します。これにより、ヒープが小さくなり、非ポインタを生かし続ける誤検知がなくなります。他の関連する変更もヒープサイズを削減し、前のリリースに比べて全体で10%〜30%小さくなっています。その結果、スタックはもはやセグメント化されておらず、「ホットスプリット」問題が解消されます。スタックの制限に達した場合、新しい大きなスタックが割り当てられ、ゴルーチンのすべてのアクティブなフレームがそこにコピーされ、スタックへのポインタが更新されます。パフォーマンスは場合によっては顕著に向上し、常に予測可能です。詳細は設計ドキュメント で入手できます。

連続スタックの使用により、スタックはパフォーマンスの問題を引き起こすことなく小さく開始できるため、1.4でのゴルーチンのスタックのデフォルト開始サイズは8192バイトから2048バイトに減少しました。1.5リリースで予定されている並行ガベージコレクタの準備として、ヒープ内のポインタ値への書き込みは、値を更新する関数から直接ではなく、書き込みバリアと呼ばれる関数呼び出しによって行われるようになりました。

この次のリリースでは、ガベージコレクタが実行中にヒープへの書き込みを仲介できるようになります。この変更は1.4のプログラムに対して意味的な影響はありませんが、コンパイラと結果のパフォーマンスをテストするためにリリースに含まれました。

インターフェース値の実装が変更されました。以前のリリースでは、インターフェースには、格納されている具体的なオブジェクトの型に応じて、ポインタまたは1ワードのスカラー値のいずれかであるワードが含まれていました。この実装はガベージコレクタにとって問題があったため、1.4以降、インターフェース値は常にポインタを保持します。実行中のプログラムでは、ほとんどのインターフェース値はポインタでしたので、影響は最小限ですが、インターフェースに整数(例えば)を格納するプログラムは、より多くの割り当てを目にするでしょう。Go 1.3以降、ランタイムは有効なポインタを含むべきメモリワードが明らかに無効なポインタ(例えば、値3)を含んでいる場合にクラッシュします。ポインタ値に整数を格納するプログラムは、このチェックに引っかかりクラッシュする可能性があります。Go 1.4では、GODEBUG 変数invalidptr=0を設定することで、回避策としてクラッシュを無効にできますが、将来のリリースでクラッシュを回避できる保証はありません。正しい修正は、整数とポインタをエイリアスしないようにコードを書き直すことです。

アセンブリ

アセンブラcmd/5acmd/6acmd/8aが受け入れる言語にはいくつかの変更があり、主にランタイムに型情報を提供しやすくするためのものです。まず、TEXTディレクティブのフラグを定義するtextflag.hファイルがリンカーのソースディレクトリから標準の場所にコピーされ、簡単なディレクティブで含めることができるようになりました。

c
1
#include "textflag.h"

より重要な変更は、アセンブリソースが必要な型情報を定義する方法にあります。ほとんどのプログラムでは、データ定義(DATAおよびGLOBLディレクティブ)をアセンブリからGoファイルに移動し、各アセンブリ関数に対してGo宣言を書くことで十分です。アセンブリドキュメント に何をすべきかが記載されています。更新: 古い場所からtextflag.hを含むアセンブリファイルはまだ動作しますが、更新するべきです。型情報については、ほとんどのアセンブリルーチンは変更を必要としませんが、すべてを確認する必要があります。データを定義するアセンブリソースファイル、非空のスタックフレームを持つ関数、またはポインタを返す関数は特に注意が必要です。必要な(しかし簡単な)変更の説明はアセンブリドキュメント にあります。これらの変更に関する詳細情報はアセンブリドキュメント にあります。

gccgoのステータス

GCCとGoプロジェクトのリリーススケジュールは一致していません。GCCリリース4.9にはGo 1.2バージョンのgccgoが含まれています。次のリリース、GCC 5にはおそらくGo 1.4バージョンのgccgoが含まれるでしょう。

内部パッケージ

Goのパッケージシステムは、プログラムをクリーンな境界を持つコンポーネントに構造化するのを容易にしますが、アクセスの形式はローカル(未エクスポート)とグローバル(エクスポート済み)の2つしかありません。時には、インターフェースのクライアントを取得することを避けるために、公開リポジトリの一部であるがプログラム外での使用を意図していないコードに対してエクスポートされないコンポーネントを持ちたいことがあります。Go言語にはこの区別を強制する力はありませんが、Go 1.4からは、go コマンドが「内部」パッケージを定義するメカニズムを導入し、これらのパッケージはそれが存在するソースサブツリー外のパッケージによってインポートされることはできません。このようなパッケージを作成するには、internalという名前のディレクトリまたはinternalという名前のディレクトリのサブディレクトリに配置します。goコマンドがパスにinternalを含むパッケージのインポートを検出すると、そのインポートを行っているパッケージがinternalディレクトリの親にルートを持つツリー内にあることを確認します。例えば、パッケージ.../a/b/c/internal/d/e/fは、.../a/b/cにルートを持つディレクトリツリー内のコードによってのみインポートできます。.../a/b/gや他のリポジトリのコードによってインポートすることはできません。Go 1.4では、内部パッケージメカニズムはメインのGoリポジトリに対して強制されます。1.5以降は、任意のリポジトリに対して強制されます。メカニズムの詳細は設計ドキュメント にあります。

正規のインポートパス

コードはしばしばgithub.comのような公共サービスによってホストされるリポジトリに存在し、パッケージのインポートパスはホスティングサービスの名前で始まります。例えば、github.com/rsc/pdfです。既存のメカニズムを使用して、rsc.io/pdfのような「カスタム」または「バニティ」インポートパスを提供することができますが、それはパッケージに対して2つの有効なインポートパスを作成します。これは問題です。1つのプログラムで2つの異なるパスを通じてパッケージを誤ってインポートすることがあり、無駄です。使用しているパスが古いことが認識されずにパッケージの更新を見逃すことがあります。また、パッケージを別のホスティングサービスに移動することで、古いパスを使用しているクライアントを壊すことがあります。Go 1.4は、Goソースのパッケージ句に正規のインポートパスを識別する注釈を導入します。正規でないパスを使用してインポートが試みられた場合、goコマンドはインポートパッケージのコンパイルを拒否します。構文は簡単です。パッケージ行に識別コメントを付けます。例として、パッケージ句は次のようになります。

go
1
package pdf // import "rsc.io/pdf"

これにより、goコマンドはgithub.com/rsc/pdfをインポートするパッケージのコンパイルを拒否し、コードを移動してもユーザーを壊さないようにします。このチェックはビルド時に行われ、ダウンロード時には行われません。そのため、go getがこのチェックのために失敗した場合、誤ってインポートされたパッケージはローカルマシンにコピーされており、手動で削除する必要があります。この新機能を補完するために、ローカルパッケージのリモートリポジトリがそのカスタムインポートと一致するかどうかを確認するチェックが更新時に追加されました。go get -uコマンドは、リモートリポジトリが最初にダウンロードされたときから変更されている場合、パッケージの更新に失敗します。新しい-fフラグはこのチェックをオーバーライドします。詳細は設計ドキュメント にあります。

サブリポジトリのインポートパス

Goプロジェクトのサブリポジトリ(code.google.com/p/go.toolsなど)は、code.google.com/p/go.golang.org/x/に置き換えたカスタムインポートパスで利用できるようになりました。例えば、golang.org/x/toolsです。2015年6月1日頃にコードに正規のインポートコメントを追加し、その時点でGo 1.4以降は古いcode.google.comパスを受け入れなくなります。更新: サブリポジトリからインポートするすべてのコードは、新しいgolang.orgパスを使用するように変更する必要があります。Go 1.0以降は新しいパスを解決してインポートできるため、更新しても古いリリースとの互換性は壊れません。更新されていないコードは、2015年6月1日頃にGo 1.4でコンパイルが停止します。

go generateサブコマンド

goコマンドには、新しいサブコマンドgo generate が追加され、コンパイル前にソースコードを生成するツールの実行を自動化します。例えば、.yファイルに対してyacc コンパイラコンパイラを実行して文法を実装するGoソースファイルを生成したり、golang.org/x/toolsサブリポジトリの新しいstringer ツールを使用して型付き定数のStringメソッドの生成を自動化したりできます。詳細は設計ドキュメント を参照してください。

ファイル名の取り扱いの変更

ビルド制約、またはビルドタグとして知られるものは、ファイルを含めたり除外したりすることでコンパイルを制御します(ドキュメント/go/build を参照)。コンパイルは、ファイル自体の名前によっても制御でき、アーキテクチャまたはオペレーティングシステムの名前をアンダースコアで接続したサフィックス(.goまたは.s拡張子の前)でファイルを「タグ付け」することによって行われます。例えば、ファイルgopher_arm.goは、ターゲットプロセッサがARMの場合にのみコンパイルされます。Go 1.4以前では、単にarm.goと呼ばれるファイルも同様にタグ付けされていましたが、この動作は新しいアーキテクチャが追加されたときにソースを壊し、ファイルが突然タグ付けされることがあります。したがって、1.4では、タグ(アーキテクチャまたはオペレーティングシステム名)がアンダースコアで先行されている場合にのみ、ファイルがこの方法でタグ付けされます。更新: 古い動作に依存するパッケージはもはや正しくコンパイルされません。windows.goamd64.goのような名前のファイルは、ソースに明示的なビルドタグを追加するか、os_windows.gosupport_amd64.goのように名前を変更する必要があります。

goコマンドのその他の変更

cmd/go コマンドには、注目すべきいくつかの小さな変更があります。

cgo を使用してパッケージをビルドしていない限り、goコマンドはCソースファイルのコンパイルを拒否するようになりました。これは、関連するCコンパイラ(6c など)が将来のリリースでインストールから削除される予定であるためです。(今日では、ランタイムの一部をビルドするためにのみ使用されています。)いずれにせよ正しく使用するのは難しいため、現存する使用はおそらく誤っている可能性が高く、それらを無効にしました。go test サブコマンドには、新しいフラグ-oがあり、他のサブコマンドの同じフラグに対応する結果のバイナリの名前を設定します。機能しない-fileフラグは削除されました。go test サブコマンドは、パッケージ内のすべての*_test.goファイルをコンパイルおよびリンクします。たとえそれらにテスト関数がない場合でもです。以前はそのようなファイルを無視していました。go build サブコマンドの-aフラグの動作は、非開発インストールに対して変更されました。リリースされたディストリビューションを実行しているインストールでは、-aフラグはもはや標準ライブラリとコマンドを再ビルドせず、インストールのファイルを上書きしないようにします。

パッケージソースレイアウトの変更

メインのGoソースリポジトリでは、パッケージのソースコードはsrc/pkgディレクトリに保持されていましたが、これは理にかなっていましたが、Goサブリポジトリを含む他のリポジトリとは異なっていました。Go 1.4では、ソースツリーのpkgレベルがなくなり、例えば、fmt パッケージのソースは、以前はsrc/pkg/fmtディレクトリに保持されていましたが、現在は1レベル上のsrc/fmtにあります。更新: ソースコードを発見するgodocのようなツールは、新しい場所について知る必要があります。Goチームによって維持されているすべてのツールとサービスは更新されています。

SWIG

このリリースのランタイムの変更により、Go 1.4はSWIG 3.0.3を必要とします。

雑多

標準リポジトリのトップレベルのmiscディレクトリには、エディタやIDEのGoサポートが含まれていました。プラグイン、初期化スクリプトなどです。これらを維持するのは時間がかかり、多くのエディタがコアチームのメンバーによって使用されていないため、外部の助けが必要でした。また、使用していないエディタに対してどのプラグインが最適かを決定する必要がありました。Goコミュニティ全体がこの情報を管理するのに適しています。したがって、Go 1.4では、このサポートがリポジトリから削除されました。代わりに、利用可能なもののキュレーションされた情報リストがwikiページ にあります。

パフォーマンス

ほとんどのプログラムは、1.3よりも1.4で同じ速度またはわずかに速く実行されます。一部はわずかに遅くなります。多くの変更があり、何を期待するかを正確に述べるのは難しいです。前述のように、ランタイムの多くはCからGoに翻訳され、ヒープサイズがいくらか減少しました。また、Goコンパイラはインライン化などの最適化が得意であるため、ランタイムをビルドするために使用されるCコンパイラよりもパフォーマンスがわずかに向上しました。ガベージコレクタは速度が向上し、ガベージが多いプログラムに対して測定可能な改善をもたらしました。一方、新しい書き込みバリアは再び速度を低下させ、通常は同じ程度ですが、その動作に応じて、一部のプログラムはやや遅くなったり速くなったりする可能性があります。パフォーマンスに影響を与えるライブラリの変更は以下に記載されています。

標準ライブラリの変更

新しいパッケージ

このリリースには新しいパッケージはありません。

ライブラリの主要な変更

bufio.Scanner

bufio パッケージのScanner 型には、カスタムsplit関数 に変更が必要な場合があるバグが修正されました。このバグにより、EOFで空のトークンを生成することが不可能でした。修正により、split関数が見る終了条件が変更されました。以前は、データがなくなるとEOFでスキャンが停止しました。1.4以降、入力が尽きた後にEOFでsplit関数が1回呼び出されるため、split関数はドキュメントで既に約束されているように、最後の空のトークンを生成できます。更新: カスタムsplit関数は、EOFでの空のトークンを望むように処理するために変更が必要かもしれません。

syscall

syscall パッケージは、コアリポジトリを維持するために必要な変更を除いて、現在凍結されています。特に、コアで使用されていない新しいまたは異なるシステムコールをサポートするために拡張されることはありません。その理由は別のドキュメント で詳しく説明されています。新しいサブリポジトリ、golang.org/x/sys が作成され、すべてのカーネルでのシステムコールをサポートするための新しい開発の場所として機能します。より良い構造を持ち、3つのパッケージがあり、それぞれがUnixWindows 、およびPlan 9 のシステムコールの実装を保持しています。これらのパッケージは、これらのオペレーティングシステムのカーネルインターフェースを反映するすべての合理的な変更を受け入れるように、より寛大に管理されます。詳細はドキュメントと上記の記事を参照してください。更新: 既存のプログラムは影響を受けません。syscallパッケージは1.3リリースからほとんど変更されていません。syscallパッケージにないシステムコールを必要とする将来の開発は、代わりにgolang.org/x/sysに基づいて構築する必要があります。

ライブラリの小さな変更

以下のリストは、主に追加のライブラリの小さな変更を要約しています。各変更についての詳細は、関連するパッケージドキュメントを参照してください。

archive/zip

Writer は、Flush メソッドをサポートするようになりました。

compress/flatecompress/gzip 、およびcompress/zlib

デコンプレッサのためのResetメソッドをサポートし、バッファを再利用してパフォーマンスを向上させることができます。compress/gzip パッケージには、マルチストリームファイルのサポートを制御するMultistream メソッドもあります。

crypto

crypto/ecdsa およびcrypto/rsa のPrivateKey型によって実装されるSigner インターフェースが追加されました。

crypto/tls

RFC 7301 で定義されたALPNをサポートするようになりました。crypto/tls パッケージは、Config 構造体の新しいCertificateForName 関数を通じて、サーバー証明書のプログラムによる選択をサポートするようになりました。また、crypto/tlsパッケージでは、サーバーがTLS_FALLBACK_SCSV をサポートし、クライアントがフォールバック攻撃を検出するのを助けます。(Goクライアントはフォールバックをまったくサポートしていないため、これらの攻撃には脆弱ではありません。)

database/sql

登録されたすべてのDrivers をリストできるようになりました。

debug/dwarf

UnspecifiedType をサポートするようになりました。

encoding/asn1

デフォルト値を持つオプションの要素は、その値を持っている場合にのみ省略されるようになりました。

encoding/csv

空の文字列を引用符で囲まなくなりましたが、データの終わりのマーカー\.(バックスラッシュドット)を引用符で囲みます。これはCSVの定義で許可されており、Postgresとの連携を改善します。

encoding/gob

unsafe操作の使用を排除するように書き直され、unsafeパッケージの使用を許可しない環境で使用できるようになりました。典型的な使用では10〜30%遅くなりますが、データの型に依存するため、場合によっては特に配列を含む場合は高速になることがあります。機能的な変更はありません。

encoding/xml

Decoder は、入力オフセットを報告できるようになりました。

fmt

マップへのポインタのフォーマットが、構造体、配列などへのポインタのフォーマットと一致するように変更されました。例えば、↦[string]int{"one": 1}は、デフォルトで16進数のポインタ値ではなく、↦[one: 1]として印刷されるようになりました。

image

Image 実装(RGBAGray など)には、一般的なAt メソッドに加えて、特化したRGBAAt およびGrayAt メソッドがあります。

image/png

エンコードに使用される圧縮レベルを制御するEncoder 型が追加されました。

math

Nextafter32 関数が追加されました。

net/http

Request 型には、HTTP基本認証スキームを使用して認証されたリクエストからユーザー名とパスワードを返す新しいBasicAuth メソッドがあります。net/http パッケージのTransport 型には、アウトバウンドTLS接続の動作をカスタマイズするための新しいDialTLS フックがあります。

net/http/httputil

ReverseProxy 型には、ログのユーザー制御を提供する新しいフィールドErrorLog があります。

os

Windowsオペレーティングシステムでシンボリックリンクを実装するようになりました。他のオペレーティングシステムでは既にこの機能があります。また、新しいUnsetenv 関数もあります。

reflect

Type インターフェースには、型が一般的な比較を実装しているかどうかを報告する新しいメソッドComparable があります。また、reflect パッケージでは、Value インターフェースがランタイムのインターフェースの実装の変更により、4ワードから3ワードになりました。これによりメモリが節約されますが、意味的な影響はありません。

runtime

他のシステムと同様に、Windowsで単調時計を実装するようになりました。runtime パッケージのMallocs カウンターは、Go 1.3で見逃された非常に小さな割り当てをカウントするようになりました。これにより、ReadMemStatsAllocsPerRun を使用するテストが、より正確な回答のために壊れる可能性があります。runtime パッケージでは、MemStats およびGCStats 構造体に、ガベージコレクションの一時停止が終了した時間の循環バッファであるPauseEnd 配列が追加されました。対応する一時停止期間は既にPauseNs に記録されています。

runtime/race

FreeBSDをサポートするようになり、これによりgo コマンドの-raceフラグがFreeBSDで動作するようになりました。

sync/atomic

新しい型Value があります。Valueは、任意の型の値の効率的なアトミックロードとストアのメカニズムを提供します。

syscall

Linuxでの実装では、Setuid およびSetgid が無効になりました。これらのシステムコールは、他のプラットフォームとは異なり、プロセス全体ではなく呼び出しスレッドで動作し、予期しない結果をもたらすためです。

testing

一連のテストを実行するためのより多くの制御を提供する新しい機能があります。
テストコードに関数:

go
1
func TestMain(m *testing.M)

testing.M
が含まれている場合、その関数が直接テストを実行する代わりに呼び出されます。M構造体には、テストにアクセスして実行するためのメソッドが含まれています。また、testing パッケージには、新しいCoverage 関数があり、現在のテストカバレッジの割合を報告し、個々のテストが全体のカバレッジにどれだけ貢献しているかを報告できます。

text/scanner

Scanner 型には、スキャン時に識別子の定義を制御できる新しい関数IsIdentRune があります。

text/template

ブール関数eqltなどは、符号付き整数と符号なし整数の比較を許可するように一般化され、実際の使用が簡素化されました。(以前は、同じ符号の値のみを比較できました。)すべての負の値は、すべての符号なし値よりも小さいと比較されます。

time

マイクロ秒の持続時間を印刷するために、マイクロプレフィックスの標準記号、マイクロ記号(U+00B5 ‘µ’)を使用するようになりました。ParseDuration はまだusを受け入れますが、パッケージはもはやマイクロ秒をusとして印刷しません。

更新: 持続時間の出力形式に依存しているが、ParseDurationを使用していないコードは更新が必要です。


スポンサーリンク

共有

もふもふ
著者
もふもふ
プログラマ。汎用系→ゲームエンジニア→Webエンジニア→QAエンジニア