Upgrade to Pro — share decks privately, control downloads, hide ads and more …

noCopyとその展望

Avatar for Shotaro Yamasaki Shotaro Yamasaki
October 31, 2025
650

 noCopyとその展望

Avatar for Shotaro Yamasaki

Shotaro Yamasaki

October 31, 2025

Transcript

  1. 構造体の値コピーをされると困る場合  ・sync系など  ・同じポインタなのに    各lockが非同期 type SafeCounter struct { mu

    sync.Mutex n *int } func (c *SafeCounter) Inc() { c.mu.Lock() *c.n++ c.mu.Unlock() } func main() { n := 0 c1 := SafeCounter{n: &n} c2 := c1 go c1.Inc() go c2.Inc() }
  2. type noCopy struct{} func (*noCopy) Lock() {} func (*noCopy) Unlock()

    {} noCopy sync.Lockerの interfaceを実装した 構造体
  3. type WaitGroup struct { noCopy noCopy // Bits (high to

    low): // bits[0:32] counter // bits[33:64] wait count state atomic.Uint64 sema uint32 } noCopy WaitGroupなどには標準で搭載 他にも Cond, atomic, mutex, pool ……..
  4. go vet Goの標準パッケージに含まれる静 的解析ツール var suite = []*analysis.Analyzer{ appends.Analyzer, asmdecl.Analyzer,

    assign.Analyzer, atomic.Analyzer, bools.Analyzer, buildtag.Analyzer, cgocall.Analyzer, composite.Analyzer, copylock.Analyzer, defers.Analyzer, directive.Analyzer, errorsas.Analyzer, // fieldalignment.Analyzer omitted: too noisy framepointer.Analyzer, httpresponse.Analyzer, hostport.Analyzer, ifaceassert.Analyzer, loopclosure.Analyzer, lostcancel.Analyzer, nilfunc.Analyzer, printf.Analyzer, // shadow.Analyzer omitted: too noisy shift.Analyzer, sigchanyzer.Analyzer, slog.Analyzer, stdmethods.Analyzer, stdversion.Analyzer, stringintconv.Analyzer, structtag.Analyzer, tests.Analyzer, testinggoroutine.Analyzer, timeformat.Analyzer, unmarshal.Analyzer, unreachable.Analyzer, unsafeptr.Analyzer, unusedresult.Analyzer, waitgroup.Analyzer, }
  5. copylock inspect.WithStack(nodeFilter, func(node ast.Node, push bool, stack []ast.Node) bool {

    if !push { return false } switch node := node.(type) { case *ast.File: goversion = versions.FileVersion(pass.TypesInfo, node) case *ast.RangeStmt: checkCopyLocksRange(pass, node) case *ast.FuncDecl: checkCopyLocksFunc(pass, node.Name.Name, node.Recv, node.Type) case *ast.FuncLit: checkCopyLocksFunc(pass, "func", nil, node.Type) case *ast.CallExpr: checkCopyLocksCallExpr(pass, node) case *ast.AssignStmt: checkCopyLocksAssign(pass, node, goversion, parent(stack)) case *ast.GenDecl: checkCopyLocksGenDecl(pass, node) case *ast.CompositeLit: checkCopyLocksCompositeLit(pass, node) case *ast.ReturnStmt: checkCopyLocksReturnStmt(pass, node) } return true }) return 代入 変数宣言 などをそれぞれチェック
  6. copylock // Construct a sync.Locker interface type. func init() {

    nullary := types.NewSignatureType(nil, nil, nil, nil, nil, false) // func() methods := []*types.Func{ types.NewFunc(token.NoPos, nil, "Lock", nullary), types.NewFunc(token.NoPos, nil, "Unlock", nullary), } lockerType = types.NewInterface(methods, nil).Complete() } init()でsync.LockerのInterface typeを生成 type.Implementsで確認
  7. go versionとの関係 // In go1.10, sync.noCopy did not implement Locker.

    // (The Unlock method was added only in CL 121876.) // TODO(adonovan): remove workaround when we drop go1.10. if typesinternal.IsTypeNamed(typ, "sync", "noCopy") { return []string{typ.String()} } go 1.10ではsync.noCopyがUnlock()を実装してい なかったので、sync.noCopyなら条件を満たすよう になっている
  8. go versionとの関係 if assign.Tok == token.DEFINE && versions.AtLeast(goversion, versions.Go1_22) {

    if parent, _ := parent.(*ast.ForStmt); parent != nil && parent.Init == assign { for _, l := range lhs { if id, ok := l.(*ast.Ident); ok && id.Name != "_" { if obj := pass.TypesInfo.Defs[id]; obj != nil && obj.Type() != nil { if path := lockPath(pass.Pkg, obj.Type(), nil); path != nil { pass.ReportRangef(l, "for loop iteration copies lock value to %v: %v", astutil.Format(pass.Fset, l), path) } } } } } } go 1.22以降ではforループ修正により、イテレートごと に新しく変数が宣言されるようになった。 1.22以降は新しくチェック
  9. // noCopy may be added to structs which must not

    be copied // after the first use. // // See https://golang.org/issues/8005#issuecomment-190753527 // for details. // // Note that it must not be embedded, due to the Lock and Unlock methods. type noCopy struct{} // Lock is a no-op used by -copylocks checker from `go vet`. func (*noCopy) Lock() {} func (*noCopy) Unlock() {}