给即将进行Golang开发的Ruby程序员们
背景 – 背景资料
-
- APIサーバー開発などを今までRuby(主にRails)で開発していたRubyistがサーバーサイド全部をフルGolangで実装している会社に転職して、2週間が経ち今までのGolang開発で学んだことをまとめました。
-
- これからGolang開発をするRubyistたちが、「自分と同じようにつまずくかもしれない」または「Golangを本格的に書き始める前に知っておきたかったなあ」と思うところがまとめてあります。
-
- 最近、Ruby on RailsやRuby開発を進めている会社でも、必要な箇所でGoLang開発を行なっていると聞いたので、Golang開発に興味があるRubyistが大勢いるじゃないかと思い、転職をきっかけに記事を書かせていただきました。
-
- 「Golang開発やってみたい!」「会社で導入することになった」というRubyistたちの最初のステップになれば幸いです。
- Rubyはこうだけど、Golangだとこういう風に書くよと比較のコードも混ぜています。
目标
- Rubyを書いたことがありGolangを書いたことがないけど、これからGolang開発始めよう、または興味がある人。
如何安装?
goenv をおすすめします。
rbenv とほぼ同じと思ってもらっていいです。
バージョンの切り替えも簡単にできます。
local と global のオプションもあるので、プロジェクトごとやデフォルトでもバージョン設定も可能です。
rehash と入力すると、 .ruby-version と同じく.go-version というファイルもできます。プロジェクトでなんのバージョンが使用されているのかチーム内で共有することができます。
$ git clone https://github.com/dataich/goenv.git ~/.goenv
# ログインシェルの設定ファイルに
export PATH="$HOME/.goenv/bin:$PATH"
eval "$(goenv init -)"
$ goenv install 1.7 # お好みのバージョン
$ goenv global 1.7
基本语法
关于类型,在这里我们将省略不谈。
-
- 在Go中,变量和函数使用驼峰命名法。
-
- 而Ruby使用蛇形命名法。
-
- 在Go中,想要公开的属性/函数使用大写字母开头。想要私有化的属性/函数使用小写字母开头,并且不能从不同的包中访问。在Go中,写类似于Ruby中的破坏性方法时,必须将结构体(类似于Ruby中的类)的指针作为接收者。
-
- 由于Ruby中没有指针的传值,所以我认为学习Go时最初会在这个问题上遇到困难。
-
- iota用于生成无类型整数的连续编号。
-
- 我认为这可能会有所争议,但由于这是其他语言中没有的语法,所以我想介绍一下。
-
- 通常我们会根据int创建常量类型,以便区分不同的类型。
-
- 在Go中,使用const声明常量。
-
- 在Go中,有几种创建结构体实例的方法。
-
- 使用new可以创建空指针类型的结构体实例。
- 使用结构体名{属性: 值}可以创建空结构体。
package main
import "fmt"
// 定数
const (
NonPremium = iota
Premium
)
type User struct {
firstName string
PremiumStatus int
}
// インスタンスメソッド (レシーバー名 関数名(引数) 返り値
// 構造体の中身を変更させるものなので、レシーバーをポインタ型にしないといけない。
func (u *User) UpdateToPremium() {
u.PremiumStatus = Premium
}
func main() {
user := &User{FirstName: "hoge", PremiumStatus: 0}
// これだと、ポインタ型の空の構造体を作る。FirstNameとPremiumStatusには、空文字と0が入る。
// user := new(User)
user.UpdateToPremium()
fmt.Println(user) // &{hoge 1}
fmt.Println(user.firstName) // hoge
}
尽管这样也能运行,但通常情况下,我们会创建一个类型,而不直接将iota作为int使用。
type PremiumStatus int
// 定数
// NonPremiumとPremiumはPremiumStatus型になるので
// int変数には保存できなくなる
const (
NonPremium PremiumStatus = iota
Premium
)
type User struct {
firstName string
PremiumStatus PremiumStatus
}
func main() {
user := &User{FirstName: "hoge", PremiumStatus: NonPremium}
}
class User
# 定数
NON_PREMIUM = 0
PREMIUM = 1
def initialize(first_name, premium_status)
@first_name = first_name
@premium_status = premium_status
end
private
# 破壊的メソッド
def update_to_premium!
@premium_status = PREMIUM
end
end
user = User.new('hoge', NON_PREMIUM)
user.update_to_premium!
其他语法
1. 可变长变量 (kě shù)
- 便利なので、Golangでの書き方を共有
package main
import "fmt"
func main(){
SplatOperator("a", "b", "c")
}
func SplatOperator(args ...string){
fmt.Println(args) // => [a b c]
}
def splat_operator(*args)
p args
end
splat_operator('a', 'b', 'c') # => [a b c]
我自己犯的错误集
1. Golang中使用双引号和反引号来表示。
-
- Golangではダブルクオテーションとバッククオートを使いますが意味が違います。
-
- 自分がRubyを書くとき、文字列の中で変数を展開するとき以外は、シングルクオテーションを使いますが、その癖でビルドしてみるとエラーの嵐が…。
-
- シングルクオテーションのほうが速度が上とシングルクオテーションを書くRubyistは注意です。
-
- バッククオートはエスケープシーケンスを解釈しないかつ、改行はそのままテキスト中でも改行になるため、ヒアドキュメント(複数行対応可)です。
- 式展開はどちらもないです。
package main
import "fmt"
func main(){
fmt.Println('hoge') // これはエラー
}
2. 无法从指针类型的切片中获取元素。
-
- 2週間でここに一番詰まりました。
-
- Golangには、ポインタの概念があり、最初Rubyistがつまずくのかなと思っています。
-
- Golangには、「スライス型」という可変長配列があります。
配列もありますが、それは要素数などが決められていて、実際に業務で書く時には、要素が固定で決められることはあまりないと思うので、このスライス型がよく使われます。
実際につまづいたエラーは下のものになります。
# command-line-arguments
./sample.go:7:18: invalid operation: fuga[0] (type *[]int64 does not support indexing)
package main
import "fmt"
func main(){
hoge := []int64{2, 3, 4} // => スライス型を定義
fuga := &hoge // => わざとポインタ型にしています。
fmt.Println(fuga[0]) // => invalid operation: fuga[0] (type *[]int64 does not support indexing)
}
-
- エラー文にあるように、ポインタ型のスライスはインデックスをサポートしていない = 使えないよということです。
- なので、ポインタ型から値型に戻さないといけないです。
package main
import "fmt"
func main(){
hoge := []int64{2, 3, 4}
fuga := &hoge // => わざとポインタ型にしています。
fmt.Println(*fuga[0]) // => 2 ポインタから値に戻しています。
}
在实例方法中,不能使用this或者self作为接收者的名称。
receiver name should be a reflection of its identity; don't user generic name as such or this
-
- これはエラーではなく、golintで注意されたwarningです。
- 下のようなUser構造体を作り、インスタンスメソッドを生やして、自身のオブジェクトにアクセスするときに、Rubyだと self と書いてしまうと思います。
class User < ApplicationRecord
def name_with_prefix
"Mr. #{self.name}"
end
end
-
- Golangでは、レシーバーの名前は、構造体の最初の文字にするというルールがあるので、それに従いましょう。
https://github.com/golang/go/wiki/CodeReviewComments#receiver-names
package model
type User struct{
Name string
Gender int64
}
func (s *Student) NameWithPrefix() string {
return "Mr. " + s.Name
}
// これはwarning
// func (self *Student) NameWithPrefix() string {
// return "Mr." + self.Name ここはwarning
// }
有许多不同的方法来创建结构体的实例。
- Rubyだとクラスのインスタンスを作る方法は、 下のように new しかないですよね。
class Sample
attr_reader :title
def initialize(title)
@title = title
end
end
sample = Sample.new('sample')
p sample.title
-
- しかし、Golangでは、下のような構造体があったときに、下の数だけ空の構造体を取得するやり方があります。Rubyistに気をつけてほしいのが、 newはポインタ型の構造体 を返すことです。
- Rubyではnewでインスタンス取得できると思いますが、Rubyにはポインタの概念がないので、Golangでnewとすると、ポインタ型ではない空の構造体が取得できると思われがちですが、ポインタ型なので注意が必要です。
type Vehicle struct{
Name string
}
package main
func main(){
// 同じ意味 空の構造体を返す
var v Vehicle = Vehicle{}
vehicle := Vehicle{}
// 同じ意味 Golangでの`new`は、 ポインタ型を返すので注意
vehicle := new(Vehicle)
vehicle := &Vehicle{}
}
5. 地图初始化
-
- ここでいうMapは、Rubyでいうハッシュです。
-
- 注意してほしいのは、Mapは宣言に加えて、一度初期化を行わないといけないことです。
-
- Rubyだと初期化する必要は全くない & Map型だけ初期化が必要になってくるので、ここでまとめておきました。
- 参考: http://otiai10.hatenablog.com/entry/2014/08/09/154256
panic: runtime error: assignment to entry in nil map
package main
import "fmt"
func main() {
var m map[string]string
fmt.Println(m) // map[]
/* panic: runtime error: assignment to entry in nil map
m["hoge"] = "fuga"
fmt.Println(m)
*/
// 初期化すればOK
m = map[string]string{}
fmt.Println(m) // map[]
m["hoge"] = "fuga"
fmt.Println(m) // map[hoge:fuga]
}
总结
-
- たった2週間ですが、まとめてみると多くのところに詰まったと思っています。
-
- 時間ができたときは、さらに自分が詰まったところを残しておこうと思います。
- これからGolangデビューするRubyistの参考になれば幸いです。