Attributes
Attributes attach metadata or behavior to declarations.
Struct tags
Section titled “Struct tags”#[json]
Section titled “#[json]”Add #[json] to a struct to generate Go JSON struct tags for all fields:
#[json]struct User { name: string, age: int, active: bool,}Generated Go:
type User struct { Name string `json:"name"` Age int `json:"age"` Active bool `json:"active"`}Serialization attributes accept options:
| Option | Effect |
|---|---|
omitempty | Omit field if empty |
!omitempty | Include field if empty |
omitzero | Omit field if it holds a zero value |
!omitzero | Include field if it holds a zero value |
skip | Exclude field |
snake_case | Convert field name to snake_case |
camel_case | Convert field name to camelCase |
string | Encode numbers as strings |
omitempty has no effect on a struct, enum, tuple, or Array<T, N> field with N greater than zero. None of them is one of Go’s empty values, so use omitzero for those.
#[json]struct Config { #[json(omitempty)] timeout: Option<int>,omitted if None
#[json(skip)] internal_id: int,never serialized
#[json(string)] large_number: int64,encoded as "1234", not 1234}Struct-level options apply to all fields. A field-level option overrides them, and an explicit name overrides both:
#[json(snake_case)]struct UserProfile { userName: string,"user_name" from snake_case
createdAt: int,"created_at" from snake_case
#[json("userID")] id: int,"userID", snake_case overridden}Other serializers
Section titled “Other serializers”More supported attributes:
#[xml]#[yaml]#[toml]#[db]#[bson]#[mapstructure]#[msgpack]Attributes stack:
#[json]#[db]struct User { #[json("userName")] #[db("user_name")] name: string,}#[tag]
Section titled “#[tag]”For custom tags, use #[tag]:
#[json]struct Input { #[tag("validate", "required")] email: string,}Generated Go:
type Input struct { Email string `json:"email" validate:"required"`}For more complex tags, use a backticked string:
#[json]struct User { #[tag(`validate:"required,email" gorm:"unique"`)] email: string,}Generated Go:
type User struct { Email string `json:"email" validate:"required,email" gorm:"unique"`}Generated code
Section titled “Generated code”#[iterate]
Section titled “#[iterate]”Add #[iterate] to an enum to synthesize a variants() associated function returning every variant, in declaration order:
#[iterate]enum Direction { North, East, West, South,}
for direction in Direction.variants() { fmt.Println(direction)prints North, East, West, South}#[iterate] works only on enums whose variants carry no data.
#[display]
Section titled “#[display]”By default a struct or enum has no display form. Printing one falls back to Go’s %v, and interpolating one in an f-string is rejected.
struct Point { x: int, y: int,}
let p = Point { x: 1, y: 2 }
fmt.Println(p)prints {1 2}fmt.Println(f"at {p}")error: Point has no display formAdd #[display] to render it as a readable string instead.
#[display]struct Point { x: int, y: int,}
let p = Point { x: 1, y: 2 }
fmt.Println(p)prints Point { x: 1, y: 2 }#[display] also gives the enum or struct a to_string() method.
interface Display { fn to_string() -> string}
fn render(value: Display) -> string { value.to_string()}
render(Point { x: 1, y: 2 })Point satisfies Display#[equality]
Section titled “#[equality]”== and != work on natively comparable types: primitives, arrays whose elements are comparable, and structs, enums, and tuples whose components are all comparable.
struct User { name: string, age: int,}
let u1 = User { name: "Alice", age: 30 }let u2 = User { name: "Alice", age: 30 }
u1 == u2trueOther types are not natively comparable: slices, maps, functions, interfaces, and any array, struct, enum, or tuple that contains a non-comparable value.
struct Order { id: int, tags: Slice<string>,not natively comparable}
let o1 = Order { id: 1, tags: ["a"] }let o2 = Order { id: 1, tags: ["a"] }
o1 == o2error: Order cannot be compared with ==For maps and slices, use the built-in equals() method:
let a = [1, 2, 3]let b = [1, 2, 3]
a.equals(b)trueTo enable comparison on types that are not natively comparable and do not have a built-in equals() method, mark them with the #[equality] attribute. This will auto-generate an equals() method to compare the type structurally.
#[equality]struct Order { id: int, tags: Slice<string>,}
let a = Order { id: 1, tags: ["a"] }let b = Order { id: 1, tags: ["a"] }
a.equals(b)trueThe auto-generated equals() method compares by these rules:
==for comparable fields.equals()for slice and map fields- the field type’s own
equalsfor nested#[equality]types
If you need a custom comparator, write an equals method yourself:
struct Fraction { numerator: int, denominator: int,}
impl Fraction { fn equals(self, other: Fraction) -> bool { self.numerator * other.denominator == other.numerator * self.denominator }}
let a = Fraction { numerator: 1, denominator: 2 }let b = Fraction { numerator: 2, denominator: 4 }
a.equals(b)true#[equality] works on generic structs and enums, as long as the type parameter is bound by Comparable or Ordered.
#[equality]struct Cart<T: Comparable> { items: Slice<T>}
let a = Cart { items: [1, 2, 3] }let b = Cart { items: [1, 2, 3] }
a.equals(b)trueThe bound can also be a custom interface instead, as long as the interface declares an equals method.
interface Equatable<T> { fn equals(other: T) -> bool}
#[equality]struct Batch<T: Equatable<T>> { item: T}
let a = Batch { item: Order { id: 1, tags: ["a"] } }let b = Batch { item: Order { id: 1, tags: ["a"] } }
a.equals(b)true#[default]
Section titled “#[default]”Out of the box, an enum has no zero value.
enum Status { Active, Paused, Stopped,}
let queue = Slice.make<Status>(2)error: Status has no zero valuelet slots = Array.new<Status, 3>()error: Status has no zero valuePlace #[default] on an enum variant to make it the zero value.
enum Status { Active, Paused, #[default] Stopped,}
let queue = Slice.make<Status>(2)[Stopped, Stopped]let slots = Array.new<Status, 3>()[Stopped, Stopped, Stopped]An enum’s default appears wherever a zero value is expected.
struct Job { pub name: string, pub status: Status,}
let job = Job { name: "build", .. }job.status is Status.Stopped
let by_name = Map.new<string, Status>()let missing = by_name["absent"]Status.Stopped#[allow]
Section titled “#[allow]”#[allow(lint)] on a function silences that lint.
For most lints, place the attribute on the function whose code is flagged:
#[allow(match_on_bool)]silences the lint for this functionfn describe(ready: bool) -> string { match ready { true => "go", false => "wait", }}For unused-value lints (unused_result, unused_option, unused_literal, unused_value), place #[allow] on the function whose result is ignored, so every call to it stops warning.
import "go:os"
#[allow(unused_result)]silences the lint at every call belowfn warm_cache(path: string) -> Result<Slice<byte>, error> { os.ReadFile(path)}
fn main() { warm_cache("/config") warm_cache("/data")}For the unused-item lints (unused_function, unused_type, unused_struct_field, unused_enum_variant), place #[allow] on the flagged declaration itself. An allow on a struct or enum also covers its fields and variants.
#[allow(unused_enum_variant)]enum Direction { North, South,}
fn main() { let _ = Direction.North}