# Mergo — full documentation Source: https://github.com/darccio/mergo/tree/ff8ae09d071c4dd736f15cdab68deb5aaafb5751/docs License: BSD-3-Clause (https://dario.cat/mergo/LICENSE.txt). Each section identifies its Markdown URL for resolving relative links. --- Source: https://dario.cat/mergo/index.md # Mergo Mergo fills empty destination values from a source of the same type. It is useful for layering configuration defaults over values an application already has. It mutates the destination; it is not a general deep-copy or validation library. These guides describe **v1.0.2**. Mergo v1 is stable and frozen for production use; new features are reserved for a future major version. The [API reference](https://pkg.go.dev/dario.cat/mergo@v1.0.2) remains the reference for signatures. ## Install ```sh go get dario.cat/mergo@v1.0.2 ``` Import `dario.cat/mergo`, without a `/v1` suffix. Existing users of `github.com/imdario/mergo` should read the [migration guide](migration.md) before updating. ## Fill defaults, then override Pass a pointer to the destination and a source of the same type to `mergo.Merge`. A pointer to that source type also works. By default, non-empty destination values win. `WithOverride` gives non-empty source values priority for ordinary scalar struct fields. This runnable example imports `fmt` and `dario.cat/mergo`: ```go func ExampleMerge_defaults() { type Config struct { Host string Port int } src := Config{Host: "default.example", Port: 8080} dst := Config{Host: "custom.example"} if err := mergo.Merge(&dst, src); err != nil { panic(err) } fmt.Println(dst) if err := mergo.Merge(&dst, src, mergo.WithOverride); err != nil { panic(err) } fmt.Println(dst) // Output: // {custom.example 8080} // {default.example 8080} } ``` An empty scalar is `false`, a numeric zero, or an empty string. Maps and slices with length zero count as empty, and pointers normally inherit the emptiness of their targets. This cannot distinguish an omitted `false` or `0` from one explicitly configured by the caller. See [pointer defaults and empty values](options.md). ## Guides - [Options](options.md): all eight options, precedence, empty slices, pointer behavior, and map caveats. - [Transformers](transformers.md): merge `time.Time` using `IsZero`, choose an overwrite policy, and return validation errors. - [Migration](migration.md): move direct imports to `dario.cat/mergo`, or temporarily pin an indirect legacy dependency. - [FAQ](faq.md): map values, aliases, JSON numbers, errors, and other boundaries. ## Merge and Map `Merge` accepts same-type structs, maps, and slices. It recursively visits exported struct fields and maps, subject to the [limitations in the FAQ](faq.md). A merge does not delete destination map keys by default. `Map` maps between a struct and `map[string]interface{}`. It changes the first letter of a key to match a field, or lowercases the first letter of a field to make a map key. It does not interpret JSON tags or convert number types. Start with an initialized map when mapping a struct to a map. [The mapping example and boundaries](faq.md#can-map-convert-json-numbers-or-use-json-tags) show both directions. Use `Merge(..., WithOverride)` or `Map(..., WithOverride)` in new code. `MergeWithOverwrite` and `MapWithOverwrite` are deprecated convenience wrappers. ## Executable examples and sources The Go code blocks are examples from [docs_example_test.go](https://github.com/darccio/mergo/blob/master/docs_example_test.go). Put them in a `_test.go` file with `package mergo_test` and the imports noted above each example, or run the repository examples: ```sh go test ./... -run '^Example' -count=1 ``` Behavior in these guides is checked against the [v1.0.2 implementation](https://github.com/darccio/mergo/blob/7b33b2b01026fbbbbfcfbb1ee2c9c0a5e0c9a9f7/merge.go) and [emptiness rules](https://github.com/darccio/mergo/blob/7b33b2b01026fbbbbfcfbb1ee2c9c0a5e0c9a9f7/mergo.go). Code examples use deterministic output instead of the current time or map iteration order. ## Project links - [API reference](https://pkg.go.dev/dario.cat/mergo@v1.0.2) - [Source repository](https://github.com/darccio/mergo) - [Releases](https://github.com/darccio/mergo/releases) - [Report a bug](https://github.com/darccio/mergo/issues) - [Sponsor maintenance](https://github.com/sponsors/darccio) ## License Mergo and these docs are distributed under the [BSD 3-Clause license](LICENSE.txt). --- Source: https://dario.cat/mergo/options.md # Merge options Pass options after `dst` and `src` to `mergo.Merge` or `mergo.Map`. Options configure a merge; they do not validate every possible input. All functions below are exported in [v1.0.2](https://pkg.go.dev/dario.cat/mergo@v1.0.2). ## At a glance | Option | Effect | Important boundary | | --- | --- | --- | | `WithOverride` | Gives non-empty source scalar struct fields priority | Map entries and opaque structs have different rules | | `WithOverwriteWithEmptyValue` | Also copies empty source values; enables override | Deletes destination map keys absent from the source | | `WithOverrideEmptySlice` | Allows nil or empty source slices to be assigned | Needs `WithOverride` to clear a non-empty destination slice | | `WithoutDereference` | Treats a non-nil pointer as non-empty | Does not replace an existing pointer to a struct | | `WithAppendSlice` | Appends source slice elements after destination elements | No deduplication; takes precedence over slice replacement | | `WithTypeCheck` | Rejects different concrete slice types when replacing map-held slices | Use with `WithOverride`; not a general type checker | | `WithSliceDeepCopy` | Visits overlapping slice positions recursively; enables override | Not a deep clone; does not extend slices or assign ordinary value elements | | `WithTransformers(t)` | Installs custom behavior for selected types | A transformer replaces normal merging for its matched type | ## Override and empty values For ordinary scalar struct fields, `WithOverride` still ignores an empty source. Use `WithOverwriteWithEmptyValue` when a source `false`, `0`, empty string, or nil pointer is meant to clear the destination. Imports: `fmt` and `dario.cat/mergo`. ```go func ExampleMerge_emptyValues() { type Config struct { Enabled bool Retries int } src := Config{} dst := Config{Enabled: true, Retries: 3} if err := mergo.Merge(&dst, src, mergo.WithOverride); err != nil { panic(err) } fmt.Println(dst) if err := mergo.Merge(&dst, src, mergo.WithOverwriteWithEmptyValue); err != nil { panic(err) } fmt.Println(dst) // Output: // {true 3} // {false 0} } ``` `WithOverwriteWithEmptyValue` already enables override, so adding `WithOverride` is unnecessary. On maps it also removes destination keys missing from the source, including in nested maps. Treat it as a potentially destructive operation when you only intend to update a few fields. Map entries use different rules: `WithOverride` alone can assign a zero-valued source entry. It leaves destination-only keys in place, unlike `WithOverwriteWithEmptyValue`. Imports: `fmt` and `dario.cat/mergo`. ```go func ExampleMerge_mapOverwrite() { dst := map[string]int{"retries": 3, "keep": 1} src := map[string]int{"retries": 0} if err := mergo.Merge(&dst, src, mergo.WithOverride); err != nil { panic(err) } fmt.Println(dst["retries"], dst["keep"], len(dst)) if err := mergo.Merge(&dst, src, mergo.WithOverwriteWithEmptyValue); err != nil { panic(err) } _, kept := dst["keep"] fmt.Println(dst["retries"], kept, len(dst)) // Output: // 0 1 2 // 0 false 1 } ``` Opaque structs such as `time.Time` are another exception: without a [transformer](transformers.md), `WithOverride` can replace them with a zero struct. Do not apply the scalar rule to every type. ## Empty slices By default, a non-empty source slice can fill an empty destination slice, but an empty source does not clear a non-empty destination. `WithOverrideEmptySlice` permits assignment of an empty or nil source slice. It does not enable override by itself: combine it with `WithOverride` to clear a non-empty slice. Imports: `fmt` and `dario.cat/mergo`. ```go func ExampleMerge_emptySlice() { type Config struct{ Names []string } dst := Config{Names: []string{"local"}} src := Config{Names: []string{}} if err := mergo.Merge(&dst, src, mergo.WithOverrideEmptySlice); err != nil { panic(err) } fmt.Println(dst.Names) if err := mergo.Merge(&dst, src, mergo.WithOverride, mergo.WithOverrideEmptySlice); err != nil { panic(err) } fmt.Println(len(dst.Names), dst.Names == nil) src.Names = nil if err := mergo.Merge(&dst, src, mergo.WithOverrideEmptySlice); err != nil { panic(err) } fmt.Println(dst.Names == nil) // Output: // [local] // 0 false // true } ``` This also distinguishes an allocated empty slice from a nil slice, which can matter when encoding JSON. `WithOverwriteWithEmptyValue` already allows empty-slice assignment and clearing. These descriptions apply to top-level slices and slice fields. Map-held slices follow the map branch's rules: `WithOverride` alone can replace them with empty source slices. Check the behavior for your map's concrete value types rather than treating the struct-field rule as universal. ## Append slices `WithAppendSlice` appends destination elements followed by source elements, preserving duplicates. It applies even when the destination is non-empty, and adding `WithOverride` does not turn it into replacement. Imports: `fmt` and `dario.cat/mergo`. ```go func ExampleMerge_appendSlice() { dst := []string{"local", "shared"} src := []string{"shared", "default"} if err := mergo.Merge(&dst, src, mergo.WithOverride, mergo.WithAppendSlice); err != nil { panic(err) } fmt.Println(dst) // Output: [local shared shared default] } ``` Appending can reuse the destination backing array. Pointer, slice, and map elements can still share data; see [aliasing](faq.md#does-a-merge-copy-data-or-share-it). ## Traverse slice positions Despite its name, `WithSliceDeepCopy` is an element-wise merge, not a deep-copy operation. It enables override for the whole merge and visits indices up to the shorter slice length. It neither appends source-only positions nor removes destination-only positions. A useful case is a slice of non-nil pointers to structs: the existing destination objects can be modified without changing pointer identity. Empty scalar source fields still preserve destination fields. Imports: `fmt` and `dario.cat/mergo`. ```go func ExampleMerge_sliceDeepCopy() { type Item struct{ Name string } first := &Item{Name: "local"} dst := []*Item{first, {Name: "keep"}} src := []*Item{{Name: "source"}, {Name: ""}, {Name: "ignored"}} if err := mergo.Merge(&dst, src, mergo.WithSliceDeepCopy); err != nil { panic(err) } fmt.Println(len(dst), dst[0].Name, dst[1].Name) fmt.Println(dst[0] == first, dst[0] == src[0]) values := []int{1} if err := mergo.Merge(&values, []int{2, 3}, mergo.WithSliceDeepCopy); err != nil { panic(err) } fmt.Println(values) // Output: // 2 source keep // true false // [1] } ``` The last line shows a limitation: ordinary scalar slice elements are not assigned by this option in v1.0.2. Value-struct elements have the same addressability problem. Maps and pointer targets can be modified, but this option does not promise independent data or support arbitrary heterogeneous slices. Use ordinary slice replacement or explicit copying when that is the intended behavior. [The implementation](https://github.com/darccio/mergo/blob/7b33b2b01026fbbbbfcfbb1ee2c9c0a5e0c9a9f7/merge.go) defines these bounds. If `WithAppendSlice` and `WithSliceDeepCopy` are both set, append wins for slices. Prefer choosing one slice policy per call. ## Pointer defaults and identity By default, a non-nil pointer to a zero value is considered empty, and merging can mutate its target. `WithoutDereference` makes a non-nil pointer count as present, preserving a caller's explicit `false` or `0` during a default merge. For scalar pointers, `WithOverride` plus `WithoutDereference` assigns the source pointer rather than modifying the old destination target. The two values then share that pointer. Imports: `fmt` and `dario.cat/mergo`. ```go func ExampleMerge_pointerDefaults() { type Config struct{ Enabled *bool } local, fallback := false, true dst, src := Config{Enabled: &local}, Config{Enabled: &fallback} if err := mergo.Merge(&dst, src); err != nil { panic(err) } fmt.Println(local, dst.Enabled == &local) local = false if err := mergo.Merge(&dst, src, mergo.WithoutDereference); err != nil { panic(err) } fmt.Println(local, dst.Enabled == &local) if err := mergo.Merge(&dst, src, mergo.WithOverride, mergo.WithoutDereference); err != nil { panic(err) } fmt.Println(*dst.Enabled, dst.Enabled == src.Enabled, local) // Output: // true true // false true // true true false } ``` There is an important boundary for struct pointers: `WithoutDereference` does not replace two existing non-nil pointers to structs, even with `WithOverride`. An ordinary override instead merges their exported fields while retaining destination pointer identity. Imports: `fmt` and `dario.cat/mergo`. ```go func ExampleMerge_pointerStruct() { type Item struct{ Name string } type Config struct{ Item *Item } dst := Config{Item: &Item{Name: "local"}} src := Config{Item: &Item{Name: "source"}} if err := mergo.Merge(&dst, src, mergo.WithOverride, mergo.WithoutDereference); err != nil { panic(err) } fmt.Println(dst.Item.Name, dst.Item == src.Item) if err := mergo.Merge(&dst, src, mergo.WithOverride); err != nil { panic(err) } fmt.Println(dst.Item.Name, dst.Item == src.Item) // Output: // local false // source false } ``` A nil destination pointer can be assigned the source pointer. A nil source does not clear an existing pointer unless `WithOverwriteWithEmptyValue` is enabled. See the [pointer regression tests](https://github.com/darccio/mergo/blob/7b33b2b01026fbbbbfcfbb1ee2c9c0a5e0c9a9f7/issue131_test.go) and [nil-source test](https://github.com/darccio/mergo/blob/7b33b2b01026fbbbbfcfbb1ee2c9c0a5e0c9a9f7/issue149_test.go). ## Check slice types in maps Top-level `Merge` requires identical destination and source types regardless of options. A `map[string]interface{}`, however, can hold different concrete slice types under the same key. `WithOverride` plus `WithTypeCheck` rejects replacing, for example, `[]int` with `[]string`. Imports: `fmt` and `dario.cat/mergo`. ```go func ExampleMerge_typeCheck() { dst := map[string]interface{}{"items": []int{1}} src := map[string]interface{}{"items": []string{"one"}} err := mergo.Merge(&dst, src, mergo.WithOverride, mergo.WithTypeCheck) fmt.Println(err != nil) fmt.Printf("%T\n", dst["items"]) // Output: // true // []int } ``` This check is specific to the map-held slice replacement branch. It does not enforce a schema for all interface or map values, perform conversions, or make `WithSliceDeepCopy` safe for arbitrary mixed element types. Slice append checks concrete slice types even without `WithTypeCheck`. See [the v1.0.2 slice type tests](https://github.com/darccio/mergo/blob/7b33b2b01026fbbbbfcfbb1ee2c9c0a5e0c9a9f7/mergo_test.go). ## Transformers and precedence Use `WithTransformers(yourTransformer)` to choose behavior for a type such as `time.Time`. A matched callback runs instead of normal traversal; `WithOverride` and the empty-value options do not automatically alter its policy. [The transformer cookbook](transformers.md) explains how to implement that policy and return errors. The built-in options above set flags, so their order does not change these precedence rules. Multiple `WithTransformers` options replace the same configuration field: the last one wins. Combine type handlers in one implementation rather than expecting them to be chained. A custom option that writes `Config` fields can introduce its own ordering behavior. [Back to Mergo](index.md) · [FAQ](faq.md) --- Source: https://dario.cat/mergo/transformers.md # Transformer cookbook A transformer customizes merging for an exact Go type. Pass an implementation of `mergo.Transformers` to `mergo.WithTransformers`. Its `Transformer(reflect.Type)` method returns a callback for a type it handles, and `nil` for types that should use normal merging. A matching callback replaces normal processing for that value, including traversal into its children. It owns the decision to keep, replace, append, or reject the source. The callback receives reflected destination and source values, but does not receive Mergo's `Config`. This behavior follows the [v1.0.2 dispatch code](https://github.com/darccio/mergo/blob/7b33b2b01026fbbbbfcfbb1ee2c9c0a5e0c9a9f7/merge.go). ## Merge time.Time using IsZero `time.Time` has unexported fields and its own `IsZero` method. A default merge does not fill a zero `time.Time` field, while `WithOverride` can replace a non-zero time with a zero source. A transformer makes the intended rule explicit. [Issue #52](https://github.com/darccio/mergo/issues/52) and its [regression tests](https://github.com/darccio/mergo/blob/7b33b2b01026fbbbbfcfbb1ee2c9c0a5e0c9a9f7/issue52_test.go) document this distinction. This transformer fills a zero destination with a non-zero source. When its own `Overwrite` field is true, it also replaces a non-zero destination. It always preserves the destination if the source is zero. It checks `CanSet` before assigning and compares the exact type, so named types based on `time.Time` need their own handler. Imports for the following definition and example: `fmt`, `reflect`, `time`, and `dario.cat/mergo`. ```go type docsTimeTransformer struct { Overwrite bool } func (t docsTimeTransformer) Transformer(typ reflect.Type) func(dst, src reflect.Value) error { if typ != reflect.TypeOf(time.Time{}) { return nil } return func(dst, src reflect.Value) error { if !dst.CanSet() { return nil } source := src.Interface().(time.Time) destination := dst.Interface().(time.Time) if !source.IsZero() && (destination.IsZero() || t.Overwrite) { dst.Set(src) } return nil } } ``` ```go func ExampleMerge_timeTransformer() { type Snapshot struct{ Time time.Time } first := time.Date(2025, 5, 7, 12, 0, 0, 0, time.UTC) later := first.Add(time.Hour) var dst Snapshot if err := mergo.Merge(&dst, Snapshot{Time: first}, mergo.WithTransformers(docsTimeTransformer{})); err != nil { panic(err) } fmt.Println(dst.Time.Format(time.RFC3339)) if err := mergo.Merge(&dst, Snapshot{Time: later}, mergo.WithOverride, mergo.WithTransformers(docsTimeTransformer{})); err != nil { panic(err) } fmt.Println(dst.Time.Equal(first)) if err := mergo.Merge(&dst, Snapshot{Time: later}, mergo.WithTransformers(docsTimeTransformer{Overwrite: true})); err != nil { panic(err) } fmt.Println(dst.Time.Equal(later)) if err := mergo.Merge(&dst, Snapshot{}, mergo.WithTransformers(docsTimeTransformer{Overwrite: true})); err != nil { panic(err) } fmt.Println(dst.Time.Equal(later)) // Output: // 2025-05-07T12:00:00Z // true // true // true } ``` The second merge passes `WithOverride`, but the callback's `Overwrite` is false, so the first timestamp survives. The third merge sets the callback's policy to true without requiring `WithOverride`. This is deliberate: ordinary options do not automatically override a transformer's decisions. To allow a zero source to clear a time, change the transformer's condition deliberately. Do not expect `WithOverwriteWithEmptyValue` to change the callback's rule. Handle multiple special types in the same `Transformer` method; passing `WithTransformers` twice installs only the last implementation. ## Return an error Callbacks can return errors, and `Merge` returns that error to the caller. This recipe defines a named port type so the transformer only validates that field type; returning `nil` for ordinary `int` avoids validating unrelated integer fields. Imports for the following definition and example: `errors`, `fmt`, `reflect`, and `dario.cat/mergo`. ```go type docsPort int var errDocsPort = errors.New("port must be positive") type docsPortTransformer struct{} func (docsPortTransformer) Transformer(typ reflect.Type) func(dst, src reflect.Value) error { if typ != reflect.TypeOf(docsPort(0)) { return nil } return func(dst, src reflect.Value) error { if src.Int() <= 0 { return errDocsPort } if dst.CanSet() && dst.Int() == 0 { dst.Set(src) } return nil } } ``` ```go func ExampleMerge_transformerError() { type Config struct { Name string Port docsPort } var dst Config src := Config{Name: "service", Port: -1} err := mergo.Merge(&dst, src, mergo.WithTransformers(docsPortTransformer{})) fmt.Println(errors.Is(err, errDocsPort), dst.Name, dst.Port) // Output: true service 0 } ``` The example also shows that merging is **not transactional**: `Name` was assigned before the port transformer rejected the source. Map iteration order is unspecified, so do not rely on a particular partial result from a failed map merge. Validate before merging, or merge into an independent copy and replace the original only after success when atomic changes are required. A shallow struct copy does not isolate pointers, maps, or slice backing arrays. ## Reflection boundaries Match the exact type before calling type-specific reflection methods. Use `CanSet` before assigning. A value returned from a map lookup may not be addressable or settable, so a transformer does not automatically make struct map values mutable. See the [FAQ](faq.md#why-arent-struct-fields-inside-maps-merged). Mergo skips transformer dispatch for a nil destination pointer, slice, map, or interface. Normal merging may assign a source value in that case. If you need uniform validation, validate the source before merging rather than depending on a callback running for every value. [Back to Mergo](index.md) · [Options](options.md) --- Source: https://dario.cat/mergo/migration.md # Migrate to dario.cat/mergo Since v1.0.0, Mergo declares its module path as `dario.cat/mergo`. The GitHub repository location and the module's import path are different things. New code should import the declared module path. ## Recognize the module-path mismatch If an import or dependency still requires `github.com/imdario/mergo` but selects a v1 release, Go reports a module-path mismatch. [Issue #248](https://github.com/darccio/mergo/issues/248) records the transition failure. Its [dependency discussion](https://github.com/darccio/mergo/issues/248#issuecomment-1985399728) also explains why replacing the old path with the new one can fail when both are in the module graph. A `go get` alone does not rewrite your source imports. Choose the direct or indirect dependency path below. ## Direct dependency: change your imports For code you maintain, replace imports of `github.com/imdario/mergo` with `dario.cat/mergo`, install the new module, and tidy your dependencies. The public API remains available at the new path, but run your application's tests to verify the behavior you depend on. From the application's module root: ```sh rg -l --null 'github\.com/imdario/mergo' -g '*.go' -g '!vendor/**' \ | xargs -0 -r sed -i 's#github.com/imdario/mergo#dario.cat/mergo#g' go get dario.cat/mergo@v1.0.2 go mod tidy go test ./... ``` The rewrite command uses ripgrep and GNU `sed`/`xargs`, as available on Linux. On other platforms, use the editor's replace-in-files command over your own `.go` files, excluding vendored dependencies. After `go mod tidy`, `github.com/imdario/mergo` should disappear from `go.mod` unless another dependency still imports it. Check the result: ```sh go list -m all rg 'github\.com/imdario/mergo' go.mod go.sum rg 'github\.com/imdario/mergo' -g '*.go' -g '!vendor/**' ``` The old and new module can temporarily coexist if a dependency still uses the old path. Inspect the dependency chain with `go mod why -m github.com/imdario/mergo` and update that dependency when it supports the vanity path. Do not add `/v1` to the import. Go's major-version suffix convention applies from v2 onward. See [Go's module path rules](https://go.dev/ref/mod#module-path). ## Indirect dependency: pin the old path temporarily If a dependency you cannot immediately change still imports the old path, retain that path and pin it to **v0.3.16**, the final release declaring `github.com/imdario/mergo`. Add this replacement to your application's `go.mod`: ```text replace github.com/imdario/mergo => github.com/imdario/mergo v0.3.16 ``` Equivalently, from the module root: ```sh go mod edit -replace=github.com/imdario/mergo=github.com/imdario/mergo@v0.3.16 go mod tidy go test ./... ``` This keeps the legacy dependency on the legacy module. It does not upgrade that dependency to v1. Your own code may still use the `dario.cat/mergo` module at v1.0.2 alongside it. Avoid redirecting the old path to `dario.cat/mergo` with a replacement: when the graph also uses the vanity module directly, Go can reject one module version being used for two module paths. A replacement in a library's `go.mod` does not propagate to applications that depend on that library. Put a temporary replacement in the main application's module, or its active workspace as appropriate. This follows [Go's replacement rules](https://go.dev/ref/mod#go-mod-file-replace). Once the dependency updates its imports, remove the replacement and tidy again: ```sh go mod edit -dropreplace=github.com/imdario/mergo go mod tidy go test ./... ``` ## Verify resolution `go list -m dario.cat/mergo@v1.0.2` confirms the selected version. To explicitly check resolution without a module proxy: ```sh GOPROXY=direct go list -m dario.cat/mergo@v1.0.2 ``` When Go performs direct discovery for the vanity path, it requests the site's `?go-get=1` endpoint and reads its `go-import` metadata to locate GitHub. The documentation body does not participate in that discovery. See [Go's repository discovery rules](https://go.dev/ref/mod#vcs-find). [Back to Mergo](index.md) · [FAQ](faq.md) --- Source: https://dario.cat/mergo/faq.md # Mergo FAQ These answers describe v1.0.2 and link to implementation or regression tests where the behavior is easy to misread. See [Options](options.md) for the full flag reference and [Transformers](transformers.md) for type-specific policies. ## Why does a default merge ignore false, zero, or an empty string? Those values count as empty in ordinary scalar fields, so Mergo cannot distinguish an explicit zero from an omitted value. `WithOverride` still skips an empty scalar source field. `WithOverwriteWithEmptyValue` allows clearing fields, but also deletes map keys absent from the source. For a field where an explicit `false` or `0` means something different from absence, use a pointer and `WithoutDereference` during default merging. [The options examples](options.md#pointer-defaults-and-identity) demonstrate both target mutation and pointer assignment. The emptiness rules are implemented in [mergo.go](https://github.com/darccio/mergo/blob/7b33b2b01026fbbbbfcfbb1ee2c9c0a5e0c9a9f7/mergo.go). Mergo does not automatically call an arbitrary type's `IsZero` method. ## Why aren't struct fields inside maps merged? A struct returned by a map lookup is not addressable, so Mergo cannot set its fields through reflection. An existing struct map value is preserved by a default merge; `WithOverride` can replace the whole map entry instead of combining its fields. [Closed issue #90](https://github.com/darccio/mergo/issues/90) and [its regression test](https://github.com/darccio/mergo/blob/7b33b2b01026fbbbbfcfbb1ee2c9c0a5e0c9a9f7/issue90_test.go) illustrate the addressability boundary. Imports: `fmt` and `dario.cat/mergo`. ```go func ExampleMerge_mapStructValues() { type Item struct { Name string Port int } dst := map[string]Item{"service": {Name: "local"}} src := map[string]Item{"service": {Name: "source", Port: 8080}} if err := mergo.Merge(&dst, src); err != nil { panic(err) } fmt.Println(dst["service"]) if err := mergo.Merge(&dst, src, mergo.WithOverride); err != nil { panic(err) } fmt.Println(dst["service"]) // Output: // {local 0} // {source 8080} } ``` A map of non-nil pointers to structs can expose addressable targets, but then it also introduces pointer sharing and mutation. For independent values, explicitly copy a map element to a local variable, merge that variable, then assign it back. Nested maps can be merged recursively: Imports: `fmt` and `dario.cat/mergo`. ```go func ExampleMerge_nestedMaps() { dst := map[string]map[string]int{"service": {"port": 8080}} src := map[string]map[string]int{"service": {"port": 9000, "retries": 3}} if err := mergo.Merge(&dst, src); err != nil { panic(err) } fmt.Println(dst["service"]["port"], dst["service"]["retries"]) // Output: 8080 3 } ``` ## Does a merge copy data or share it? Mergo is a merge library, not a deep-cloning library. Assigning a slice copies its descriptor and can share its backing array. Assigning a pointer shares its target. Map values that contain maps, pointers, or slices can also share their referenced data. Imports: `fmt` and `dario.cat/mergo`. ```go func ExampleMerge_aliasing() { type Config struct { Names []string Item *string } name := "source" src := Config{Names: []string{"source"}, Item: &name} var dst Config if err := mergo.Merge(&dst, src); err != nil { panic(err) } dst.Names[0] = "changed" *dst.Item = "changed" fmt.Println(src.Names[0], name, dst.Item == src.Item) // Output: changed changed true } ``` `WithAppendSlice` may reuse destination slice capacity, and pointer/map/slice elements remain references. `WithSliceDeepCopy` does not allocate an independent clone: it merges existing overlapping positions and may retain references. Use explicit copying when isolation is required, and synchronize concurrent access when data is shared. ## Why doesn't WithSliceDeepCopy change my slice of structs or integers? In v1.0.2, the element-wise path rewraps interfaceable elements as reflected values. Ordinary scalar and value-struct elements then lack the settable destination required for assignment. Pointer targets and map contents can still be mutated. The option also visits only indices present in both slices; it never grows the destination. The [executable options example](options.md#traverse-slice-positions) shows the limitation alongside a working pointer-slice case. Choose append, replacement, or an explicit application-specific loop according to the desired result. Do not rely on this option as a safe general merge for heterogeneous slices. ## Why is time.Time special? `time.Time` has unexported fields. A default merge does not fill a zero time field, while `WithOverride` can copy a zero time over a non-zero time. Other opaque structs can have similar behavior. A [time transformer](transformers.md#merge-timetime-using-iszero) uses `IsZero` and makes the source-priority rule explicit. See [closed issue #52](https://github.com/darccio/mergo/issues/52). ## Can Map convert JSON numbers or use JSON tags? `Map` uses Go field names, not JSON tags. It capitalizes the first letter of map keys to locate struct fields; struct-to-map mapping lowercases the first letter of field names. Nested struct values mapped into a map remain struct values; they are not recursively converted into maps. No automatic numeric conversion takes place. For example, standard `encoding/json` decoding into `map[string]interface{}` produces `float64` numbers, which do not match an `int` or `uint16` struct field. Decode directly into a typed struct, or convert and validate values before mapping. [Closed issue #138](https://github.com/darccio/mergo/issues/138) and [its type-mismatch test](https://github.com/darccio/mergo/blob/7b33b2b01026fbbbbfcfbb1ee2c9c0a5e0c9a9f7/issue138_test.go) capture this case. Imports: `fmt` and `dario.cat/mergo`. ```go func ExampleMap() { type Config struct { Host string Port int } dst := Config{Host: "local.example"} src := map[string]interface{}{"host": "default.example", "port": 8080} if err := mergo.Map(&dst, src); err != nil { panic(err) } fmt.Println(dst) mapped := map[string]interface{}{} if err := mergo.Map(&mapped, dst); err != nil { panic(err) } fmt.Println(mapped["host"], mapped["port"]) err := mergo.Map(&dst, map[string]interface{}{"port": float64(8080)}) fmt.Println(err != nil) // Output: // {local.example 8080} // local.example 8080 // true } ``` Initialize a destination map before struct-to-map mapping. Map-to-struct handling is not identical to `Merge` in every corner case: for example, an explicit zero map value can clear a scalar field with `WithOverride`. Use typed inputs and test the behavior your application needs. The conversion paths are in [map.go](https://github.com/darccio/mergo/blob/7b33b2b01026fbbbbfcfbb1ee2c9c0a5e0c9a9f7/map.go). ## Can Mergo merge any type or private field? Top-level `Merge` accepts same-type structs, maps, and slices. It does not accept a standalone scalar as the destination. The destination must be a pointer to a usable value. Use a value or a pointer to the same type for the source. Normal field traversal does not merge unexported fields. An opaque struct may be assigned as a whole under override; that is distinct from editing its private fields. Transformers can replace an entire settable value but cannot bypass Go reflection's access rules. Mergo does not apply JSON tags, perform general conversions, or define array element-wise merging. Avoid passing typed nil pointers as top-level arguments. Validate inputs before merging when their types or presence are not already known. ## What happens when a merge returns an error? Check every returned error. Common argument errors include `ErrNonPointerArgument`, `ErrNilArguments`, `ErrDifferentArgumentsTypes`, and `ErrNotSupported`. `Map` can also report field-type mismatches; a transformer may return application-specific errors. A failure does not roll back earlier assignments. [The transformer error example](transformers.md#return-an-error) demonstrates a partially changed destination. Validate first, or work on an independent copy if success must be atomic. A shallow copy is insufficient when fields reference maps, slices, or pointer targets. ## Will v1 gain new features? Mergo v1 is stable and frozen. Report reproducible bugs with the Mergo version, Go version, input types, options, and a small test. New features are reserved for a future major version. Use [the issue tracker](https://github.com/darccio/mergo/issues) for library problems, and the [migration guide](migration.md) for old module-path errors. [Back to Mergo](index.md) · [Options](options.md) · [Transformers](transformers.md)