Repository: uber-go/zap
File: field.go (Go)

Code before:
		Interface: val,
	}
}

// Dict constructs a field containing the provided key-value pairs.
// It acts similar to [Object], but with the fields specified as arguments.
func Dict(key string, val ...Field) Field {
	return dictField(key, val)
}

// We need a function with the signature (string, T) for zap.Any.
func dictField(key string, val []Field) Field {
	return Object(key, dictObject(val))
}

type dictObject []Field

func (d dictObject) MarshalLogObject(enc zapcore.ObjectEncoder) error {
	for _, f := range d {
		f.AddTo(enc)
	}
	return nil
}

// DictObject exposes the [zapcore.ObjectMarshaler] for an array of [Field] to
// use with functions like [Object] and [Objects].
func DictObject(val ...Field) zapcore.ObjectMarshaler {
	return dictObject(val)
}

// We discovered an issue where zap.Any can cause a performance degradation
// when used in new goroutines.
//
// This happens because the compiler assigns 4.8kb (one zap.Field per arm of
// switch statement) of stack space for zap.Any when it takes the form:
//
//	switch v := v.(type) {
//	case string:
//		return String(key, v)
//	case int:
//		return Int(key, v)
//		// ...
//	default:
//		return Reflect(key, v)
//	}
//
// To avoid this, we use the type switch to assign a value to a single local variable
// and then call a function on it.
// The local variable is just a function reference so it doesn't allocate
// when converted to an interface{}.
//

Review comment (suggestion):
I would reword this a bit. Object/Objects aren't the only uses of this.

```suggestion
// DictObject constructs a [zapcore.ObjectMarshaler] with the given list of fields.
// The resulting object may be used as input to [Object], [Objects],
// or anywhere else that an object is expected.
```

Code after:
		Interface: val,
	}
}

// Dict constructs a field containing the provided key-value pairs.
// It acts similar to [Object], but with the fields specified as arguments.
func Dict(key string, val ...Field) Field {
	return dictField(key, val)
}

// We need a function with the signature (string, T) for zap.Any.
func dictField(key string, val []Field) Field {
	return Object(key, dictObject(val))
}

type dictObject []Field

func (d dictObject) MarshalLogObject(enc zapcore.ObjectEncoder) error {
	for _, f := range d {
		f.AddTo(enc)
	}
	return nil
}

// DictObject constructs a [zapcore.ObjectMarshaler] with the given list of fields.
// The resulting object marshaler can be used as input to [Object], [Objects], or
// any other functions that expect an object marshaler.
func DictObject(val ...Field) zapcore.ObjectMarshaler {
	return dictObject(val)
}

// We discovered an issue where zap.Any can cause a performance degradation
// when used in new goroutines.
//
// This happens because the compiler assigns 4.8kb (one zap.Field per arm of
// switch statement) of stack space for zap.Any when it takes the form:
//
//	switch v := v.(type) {
//	case string:
//		return String(key, v)
//	case int:
//		return Int(key, v)
//		// ...
//	default:
//		return Reflect(key, v)
//	}
//
// To avoid this, we use the type switch to assign a value to a single local variable
// and then call a function on it.
// The local variable is just a function reference so it doesn't allocate
// when converted to an interface{}.