Skip to content

Attributes

Attributes attach metadata or behavior to declarations.

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"`
}
user.go

Serialization attributes accept options:

OptionEffect
omitemptyOmit field if empty
!omitemptyInclude field if empty
skipExclude field
snake_caseConvert field name to snake_case
camel_caseConvert field name to camelCase
stringEncode numbers as strings
#[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
}

More supported attributes:

#[xml]
#[yaml]
#[toml]
#[db]
#[bson]
#[mapstructure]
#[msgpack]

Attributes stack:

#[json]
#[db]
struct User {
#[json("userName")]
#[db("user_name")]
name: string,
}

For custom tags, use #[tag]:

#[json]
struct Input {
#[tag("validate", "required")]
email: string,
}

Generated Go:

type Input struct {
Email string `json:"email" validate:"required"`
}
input.go

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"`
}
user.go

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.

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 form

Add #[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

== 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 == u2true

Other 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)true

To 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)true

The auto-generated equals() method compares by these rules:

  • == for comparable fields
  • .equals() for slice and map fields
  • the field type’s own equals for 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)true

The 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

#[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 function
fn 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 below
fn 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
}