给即将进行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

基本语法

关于类型,在这里我们将省略不谈。

    1. 在Go中,变量和函数使用驼峰命名法。

 

    1. 而Ruby使用蛇形命名法。

 

    1. 在Go中,想要公开的属性/函数使用大写字母开头。想要私有化的属性/函数使用小写字母开头,并且不能从不同的包中访问。在Go中,写类似于Ruby中的破坏性方法时,必须将结构体(类似于Ruby中的类)的指针作为接收者。

 

    1. 由于Ruby中没有指针的传值,所以我认为学习Go时最初会在这个问题上遇到困难。

 

    1. iota用于生成无类型整数的连续编号。

 

    1. 我认为这可能会有所争议,但由于这是其他语言中没有的语法,所以我想介绍一下。

 

    1. 通常我们会根据int创建常量类型,以便区分不同的类型。

 

    1. 在Go中,使用const声明常量。

 

    1. 在Go中,有几种创建结构体实例的方法。

 

    1. 使用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の参考になれば幸いです。
广告
将在 10 秒后关闭
bannerAds