Open25

Go

tomokitomoki

コンストラクタ パターン

type User struct {
    Name    string
    Age     int
}

func New(name string, age int) User {
	return User{Name: name, Age: age}
}

func main() {
    // New = コンストラクタ , user = インスタンス
    user := New("tomoki", 21)
    fmt.Println(user)
}
tomokitomoki

レシーバ

ポインタレシーバ:ポインタとして引数で渡すので、関数内でオブジェクトの値を変更できる
値レシーバ:元の値とは別のコピーした値が関数に渡されるので元の値は変更はできない

  • その構造体型のデータの値を変えたい時にポインタレシーバを使う
type Square struct {
  width  float64
  height float64
}

func (s Square) Area() float64 {
  return s.width * s.height
}
tomokitomoki

https://zenn.dev/ak/articles/1fb628d82ed79b#インターフェースを定義する

Interfaceを実装(implements)していないとerrorを出す方法

Uberもやっている手法
https://qiita.com/kskumgk63/items/423df2e5245da4b16c25

type Doer interface {
	Do()
	// Greet()
}

var _ Doer = Person{} // たったこれだけ!

type Person struct {
	Name string
}

func (p Person) Do() {
	fmt.Printf("My name is %s. I am Doer!!!!\n", p.Name)
}

func main() {
	p := Person{Name: "John"}
	p.Do()
}
tomokitomoki

Gorm

以下2つは同じ実装。複数回クエリが走る

err = tx.Preload(clause.Associations).Find(&influencer).Error
err = tx.Preload("Influencer.ActivityAreas").Preload("Influencer.Genres").Preload("Influencer").Where("uid=?", uid).First(user).Error
tomokitomoki

これで複数テーブルをjoinして取得できる

* Select("*")とすると予期しないidが返ってきた

type I struct {
	Influ
	Genre
	ActivityArea
}

var influencer *model.I = &model.I{}
err = tx.
	Table("influencers AS i").
	Select("i.*, aa.id, aa.name, g.id, g.name").
	Joins("INNER JOIN activity_area_influencers AS aai ON aai.influencer_id = i.id").
	Joins("INNER JOIN activity_areas AS aa ON aa.id = aai.activity_area_id").
	Joins("INNER JOIN genre_influencers AS gi ON gi.influencer_id = i.id").
	Joins("INNER JOIN genres AS g ON g.id = gi.genre_id").
	Where("i.id=?", 1).
	Scan(&influencer).
	Error
tomokitomoki

NG 1

influencer.ActivityAreasがinfluencer.Genresに上書きされるという事件

var influencer *model.Influencer = &model.Influencer{}
	err = tx.Table("influencers AS i").Select("*").
	Joins("INNER JOIN activity_area_influencers AS aai ON aai.influencer_id = i.id").
	Joins("INNER JOIN activity_areas AS aa ON aa.id = aai.activity_area_id").
	Joins("INNER JOIN genre_influencers AS gi ON gi.influencer_id = i.id").
	Joins("INNER JOIN genres AS g ON g.id = gi.genre_id").
	Scan(&influencer).
	Scan(&influencer.ActivityAreas).
	Scan(&influencer.Genres).
	Error
tomokitomoki

ループして配列に詰める

list := make([]string, 0, len(f.queue))
for _, key := range f.queue {
	list = append(list, key)
}
list := make([]string, 0, len(f.queue))
list = append(list, f.queue...)
tomokitomoki

関数も引数に

package main

import "fmt"

var c *Config

type Config struct {
	Func func() string
}

func main()  {
	c := &Config{
		Func: c.init,
	}
	
	method := c.Func
	method()
}

func (c *Config) init() string {
	fmt.Println("hello")
	return "init"
}
tomokitomoki

三項演算子がないので

maxItems := "1"
if recordSetIdentifier != "" {
	maxItems = "100"
}

serviceAccountName := "default"
if k6.Spec.Runner.ServiceAccountName != "" {
	serviceAccountName = k6.Spec.Runner.ServiceAccountName
}
tomokitomoki
// 最後の文字を置換する
func replaceLast(s string, laststring string) string {
	slice := strings.Split(s, "")
	slice[len(slice)-1] = laststring
	return strings.Join(slice, "")
}


// n番目の文字を置換する // nが範囲内かどうか確認する処理を入れる。
func replace(s string, new string, n int) string {
	slice := strings.Split(s, "")
	slice[n] = new
	return strings.Join(slice, "")
}

// n番目の文字を抽出する なんかミスってる
func getRuneAt(s string, i int) rune {
	rs := []rune(s)
	return rs[i]
}
tomokitomoki

base64デコードしてからファイルに書き込む

src := "SGVsbG8gV29ybGQ="

dec, err := base64.StdEncoding.DecodeString(src)
if err != nil {
	log.Fatal(err)
}

kubeconfig := flag.String("kubeconfig", filepath.Join(HomeDir(), ".kube", "config"), "(optional) absolute path to the kubeconfig file")
fmt.Println(*kubeconfig)

f, err := os.Create(*kubeconfig)
if err != nil {
	log.Println(err)
}

f.Write(dec)
tomokitomoki

木構造になっているStructのタグを全て取得する。

*めっちゃ汚い再帰関数

func main() {
    var s Users
    fetchTree(&s)
}

type Person struct {
    N *int `json:"n"`
}

type User struct {
    Person Person `json:"person"`
    Name string    `json:"name"`
    Age  int       `json:"age"`
}

type Users struct {
    User1 *User `json:"user1"`
    User2 *User `json:"user2"`
}
func fetchTree(i interface{})  {
    typ, ok := i.(reflect.Type)
    if !ok {
        typ = reflect.TypeOf(i)
    }

    switch typ.Kind() {
    case reflect.Ptr:
        get(typ.Elem())
    case reflect.Struct:
        get(typ)
    default:
    }
}

func get(typ reflect.Type)  {
    for i := 0; i < typ.NumField(); i++ {
        typ2 := typ.Field(i).Type
        
        fmt.Println(typ.Field(i).Name, typ.Field(i).Type, typ.Field(i).Type.Kind(),"-----", typ.Field(i).Tag.Get("json"))
        
        switch typ2.Kind() {
        case reflect.Ptr:
            if typ2.Elem().Kind() == reflect.Struct {
                get(typ2.Elem())
            }
        case reflect.Struct:
            get(typ2)
        default:
        }
    }
}
tomokitomoki

イメージしやすいように雑にかけば

func get(i interface{}) {
    typ, ok := i.(reflect.Type)
    if !ok {
        typ = reflect.TypeOf(i)
    }

    for i := 0; i < typ.NumField(); i++ {
        typ2 := typ.Field(i).Type

        switch typ2.Kind() {
        case reflect.Ptr:
            for i := 0; i < typ2.Elem().NumField(); i++ {
                typ3 := typ2.Elem().Field(i).Type

                switch typ3.Kind() {
                case reflect.Ptr:
                    for i := 0; i < typ3.Elem().NumField(); i++ {
                        typ4 := typ3.Elem().Field(i).Type
                        switch typ4.Kind() {
                        case reflect.Ptr:
                        case reflect.Struct:
                        default:
                        }
                    }
                case reflect.Struct:
                    // 続きがある。
                    fmt.Println(typ3)
                default:
                    fmt.Println(typ3)
                }
              
            }
        case reflect.Struct:
            // fmt.Println(typ.Field(i).Type)
        default:
            // fmt.Println()
        }
    }

}
tomokitomoki

iotaを使ってenumっぽいことを

package main

import "fmt"

type CleanupType int

const (
	All CleanupType = iota
	Succeeded
	Failed
)

// enumとして作成したtypeに対してインターフェースを実装(Stringメソッドを用意)
func (c CleanupType) String() string {
	return [...]string{"all", "succeeded", "failed"}[c]
}

func main() {
	fmt.Println(All.String())
}

https://www.youtube.com/watch?v=TOUkp_Dxb9w

文字列ベースで


tomokitomoki

Goカプセル化

package main

type Human struct {
    name string
}

// カプセル化
// メソッド名を大文字からはじめることで、publicなスコープにする。
func (human Human) GetName() string {
    return human.name
}

// セッターはレシーバをポインタにしないと値が変更されない
func (human *Human) SetName(name string) {
    human.name = name
}
tomokitomoki

Go言語のコンポジションを利用すると、struct内にinterfaceを埋め込むことで、埋め込まれたインターフェースのメソッドを利用することができます。

type Writer interface {
	Write(data []byte) error
}

type Logger struct {
	Writer
}

func (l *Logger) Log(message string) error {
	// Loggerの処理
	data := []byte(message)
	return l.Write(data)
}
tomokitomoki

providerInterfaceは、Endpoint()とVerifier(config *oidc.Config)という2つのメソッドを持つインターフェースです。これらのメソッドはoidc.Providerのメソッドと同様の機能を提供する必要があります。

providerFactoryは、ctxとissuerを引数として受け取り、providerInterfaceとエラーを返す関数の型です。providerFactoryOIDCは、oidc.NewProvider(ctx, issuer)を呼び出してproviderInterfaceを生成する具体的な実装です。

このコードの目的は、oidc.Providerの具体的な実装に依存せずに、ユニットテスト時にスタブを使用できるようにすることです。これにより、テストをより独立して実行し、コードの品質と信頼性を向上させることができます。

argo-workflows

type providerInterface interface {
	Endpoint() oauth2.Endpoint
	Verifier(config *oidc.Config) *oidc.IDTokenVerifier
}

type providerFactory func(ctx context.Context, issuer string) (providerInterface, error)

func providerFactoryOIDC(ctx context.Context, issuer string) (providerInterface, error) {
	return oidc.NewProvider(ctx, issuer)
}

func New(c Config, secretsIf corev1.SecretInterface, baseHRef string, secure bool) (Interface, error) {
	return newSso(providerFactoryOIDC, c, secretsIf, baseHRef, secure)
}