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

noCopyとその展望

Avatar for Shotaro Yamasaki Shotaro Yamasaki
October 31, 2025
99

 noCopyとその展望

Avatar for Shotaro Yamasaki

Shotaro Yamasaki

October 31, 2025
Tweet

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() {}