jinzhu/copier: Field-Level Struct Copying for Go with Tag Control
Copier for golang, copy value from struct to struct and more
At a glance
- What is it?
- jinzhu/copier is a Go library that copies values between structs, slices, and maps by matching field and method names. It handles type-mismatched pairs, provides struct tags for per-field behavior, and requires no code generation. The last push was on 2026-03-14, which is more than six months before today, so the project is no longer receiving active updates.
- Who is it for?
- jinzhu/copier is the right choice when a Go project needs to copy data between structs that share many field names and the overhead of writing manual assignment code is not justified. It is the wrong choice when correctness under all edge cases must be guaranteed at compile time, because copying errors are only surfaced at runtime.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Activity is slowing. The repository last received commits 6 months ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What jinzhu/copier Solves and Who Needs It
Go does not provide a built-in way to copy a struct into a differently typed struct even when the two types share many field names. The standard approach is to write manual assignment code field by field, which is verbose and breaks silently when a field is added to one struct but not updated in the copy routine. jinzhu/copier solves this by reflecting over both structs at runtime and copying matching fields by name. It also handles method-to-field copies: if the source has a method named DoubleAge that returns int32 and the destination has a field named DoubleAge of int32, Copier calls the method and writes the result.
The library is aimed at Go backend developers who work with separate data models at different layers, such as a database entity that must be mapped to a service struct or an API response type. The mapping logic stays out of business code and into a single Copier call. Unlike struct embedding or interface wrapping, this approach works when the source and destination types are defined in separate packages or cannot be modified.
Install and First Use
Install jinzhu/copier with the standard go get command:
go get -u github.com/jinzhu/copierImport the package and call copier.Copy with pointers to the destination and source:
import "github.com/jinzhu/copier"The following example from the README shows the basic behavior. Copier copies the Name and Age fields directly, calls the DoubleAge method on the source User to populate the DoubleAge field on Employee, and calls the Role method on Employee with the Role string from User:
user := User{Name: "Jinzhu", Age: 18, Role: "Admin"}
employee := Employee{}
copier.Copy(&employee, &user)
fmt.Printf("%#v\n", employee)
// Output: Employee{Name:"Jinzhu", Age:18, DoubleAge:36, SuperRole:"Super Admin"}The copy direction is always destination first, source second, which matches the convention of functions like io.Copy. Passing non-pointer arguments will cause a panic at runtime.
Tag-Based Field Control
jinzhu/copier provides four struct tags that change copy behavior per field. Without tags, every field with a matching name is copied and zero-value fields are overwritten.
The copier:"-" tag excludes a field from all copy operations. This is useful for fields that hold sensitive data or internal state that must not propagate to the destination struct:
type Target struct {
Name string
Secret string `copier:"-"`
}The copier:"must" tag causes a panic if the source does not have a matching non-zero value for that field. The copier:"must,nopanic" variant returns an error instead of panicking, which is safer for production code that handles errors through normal control flow:
type MandatoryTarget struct {
ID int `copier:"must"`
}The copier:"override" tag forces a field to be copied even when the CopyWithOption call sets IgnoreEmpty to true. This covers the case where a nil pointer in the source is a meaningful update that should overwrite a non-nil pointer in the destination:
type TargetOverride struct {
Details *string `copier:"override"`
}Custom field name mapping is available through a tag that names the source field:
type TargetWorker struct {
ID int64 `copier:"Identifier"`
}This maps the Identifier field from the source to the ID field in the destination without renaming either type.
Copying Slices and Maps
jinzhu/copier handles three collection copying patterns. Copying a slice to a slice produces a new slice where each element is copied individually using the same field-matching logic. Copying a struct to a slice wraps the single source struct into a one-element destination slice. Copying a map to a map works when source and destination value types are convertible:
map1 := map[int]int{3: 6, 4: 8}
map2 := map[int32]int8{}
copier.Copy(&map2, map1)
fmt.Printf("%#v\n", map2)
// Output: map[int32]int8{3:6, 4:8}Nested struct copying is supported. The README demonstrates copying an Employee struct that contains a slice of Address structs and a pointer to a Contact struct. Copier recurses into nested fields and applies the same name-matching logic at each level. Deep copying through multiple layers of pointers and slices works as long as every intermediate type has matching field names.
The go.mod file declares the minimum Go version as 1.13. Modern Go modules that require 1.21 or later can import jinzhu/copier without modification, but the module itself does not use any post-1.13 language features.
Limitations and When Not to Use It
jinzhu/copier resolves field matches at runtime using reflection. This means a misspelled field name or a type mismatch that the compiler would have caught in a manual assignment goes undetected until the code runs. In a test suite that does not exercise every code path, silent copy failures can persist for a long time.
The copier:"must" and copier:"must,nopanic" tags partially address this, but they apply to individual fields and require the developer to identify which fields matter. There is no option to make the entire copy operation fail on any unmatched field; the default behavior silently skips unmatched fields.
The library also does not support copying to unexported fields, because Go's reflect package does not allow setting unexported struct values from outside the package that defines them. Any unexported field in the destination struct is silently skipped.
Finally, the last push to the repository was on 2026-03-14. That is more than six months before this article was written, so the library is no longer actively maintained. Open issues and pull requests will not receive responses on the original repository.
Comparison with encoding/json Round-Trip and mapstructure
A common alternative to jinzhu/copier is to serialize the source struct to JSON and unmarshal it into the destination. This approach works without any external dependency, but it couples the copy behavior to JSON field tags, it is significantly slower due to serialization overhead, and it fails on any field whose type does not have a JSON representation.
mapstructure (from HashiCorp) solves the same struct-to-struct mapping problem and is more widely used in the Go ecosystem for configuration decoding. The key difference is that mapstructure is designed to convert a map[string]interface{} into a struct, while jinzhu/copier is designed to convert one typed struct into another. For layer-to-layer mapping between two named struct types, jinzhu/copier's direct field-matching approach avoids the intermediate map allocation that mapstructure requires.
For projects that need guaranteed-safe mapping and are willing to add a build step, code generation tools like struct-copy-generator produce type-safe assignment code at compile time, trading the runtime flexibility of reflection for compile-time correctness guarantees.
Editorial conclusion
jinzhu/copier is the right choice when a Go project needs to copy data between structs that share many field names and the overhead of writing manual assignment code is not justified. It is the wrong choice when correctness under all edge cases must be guaranteed at compile time, because copying errors are only surfaced at runtime. Before adopting it, verify that the Go module version in go.mod (go 1.13) is compatible with your build environment, and confirm that any fields requiring strict copy guarantees are tagged with copier:"must,nopanic" so failures return errors rather than silently succeeding.
Frequently asked questions
How does jinzhu/copier match fields between two Go structs?
Copier uses reflection to iterate over the destination struct's fields and looks for a field or method with the same name in the source. Field matching is case-sensitive by default. If the source has a method that returns a type matching the destination field, Copier calls the method and writes the return value.
What does the copier:"must" tag do in jinzhu/copier?
The copier:"must" tag causes a panic if the corresponding field in the source is empty or missing. The copier:"must,nopanic" variant returns an error instead of panicking, which lets the calling code handle the failure through normal Go error handling.
Can jinzhu/copier copy a struct into a slice?
Yes. When the destination is a pointer to a slice and the source is a single struct, Copier wraps the source in a one-element slice. When both source and destination are slices, Copier copies each element individually using the same field-matching logic.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/jinzhu-copier)