Constants & Operators
Constants in Go are compile-time values declared with const.
Introduction
Constants in Go are compile-time values declared with const. Combined with iota, they create elegant enumerations for HTTP status codes, error codes, and state machines. Operators follow familiar arithmetic, comparison, and logical rules with Go's strict typing.
Understanding operator precedence, short-circuit evaluation with && and ||, and the difference between == and deep equality for slices (via slices.Equal or reflect) prevents subtle comparison bugs in API handlers and test assertions.
This lesson builds the expression vocabulary you will use in every conditional, loop, and business rule throughout the course — from discount calculations to permission checks in middleware.
The story
A Cloudflare Workers routing configurator maps HTTP status codes and priority tiers to upstream pool weights. Constants like StatusBadGateway = 502 and bit-mask flags for feature toggles keep magic numbers out of the codebase — when an on-call engineer reads the diff at 3 AM, named constants explain intent instantly.
Operators combine arithmetic for rate limiting (requests * windowSeconds), comparisons for threshold alerts, and logical AND for feature flags — the glue between metrics and automated remediation.
Understanding the topic
Key concepts
- const declares immutable values; typed and untyped constants exist.
- iota resets to 0 in each const block and increments per line — ideal for enums.
- Arithmetic: + - * / % with integer division truncating toward zero.
- Comparison: == != < <= > >= — struct equality compares fields if comparable.
- Logical: && || ! short-circuit; bitwise: & | ^ << >> &^.
- Untyped constants participate in flexible arithmetic until assigned to typed variable.
flowchart LRA[Operands] --> Arith[+ - * /]A --> Compare[== != < >]A --> Logical[&& || !]Arith --> Result[Expression]
Step-by-step explanation
- Declare constants individually or in const ( ) blocks.
- iota starts at 0; each subsequent line increments unless reset with new const block.
- Expressions in const must be compile-time computable.
- Operators apply to typed operands; mixing types requires conversion.
- && stops if left is false; || stops if left is true.
- Bitwise ops work on integer types for flags and permissions.
Practical code example
HTTP-style status constants with iota and operator usage in authorization checks:
package mainimport "fmt"type Role intconst (RoleGuest Role = iotaRoleUserRoleAdmin)const (PermRead = 1 << iota // 1PermWrite // 2PermDelete // 4)func hasPermission(granted, required int) bool {return granted&required == required}func main() {role := RoleAdminperms := PermRead | PermWritefmt.Printf("role=%d admin=%v read=%v write=%v delete=%v\n",role, role == RoleAdmin,hasPermission(perms, PermRead),hasPermission(perms, PermWrite),hasPermission(perms, PermDelete))}
Line-by-line code explanation
const MaxRetries = 3declares an untyped constant usable in any numeric context.const ( StatusOK = 200; StatusBadGateway = 502 )groups related HTTP constants.iotain a const block auto-increments — ideal for enum-like priority levels.priority := baseWeight * (1 + retryCount)applies arithmetic with typed operands.if latencyMs > threshold && errorRate > 0.05combines comparison and logical operators.enabled := flags & FeatureRateLimit != 0uses bitwise AND to test a feature flag bit.rate := float64(success) / float64(total)requires explicit conversion between numeric types.switch { case latencyMs >= 500: ... }uses operator expressions in switch cases without a tag.
Key takeaway: iota enums are idiomatic Go — no separate enum keyword. Bit flags with iota and bitwise ops model RBAC permissions cleanly.
Real-world use
Where you'll use this in production
- HTTP status codes and error code enumerations in API packages.
- State machines for order lifecycle (Pending, Shipped, Delivered).
- Feature flags and permission bitmasks in authorization middleware.
- Time constants (time.Second, time.Minute) built with typed duration arithmetic.
Best practices
- Use iota for related constant groups; add String() method for debug output.
- Name constants in PascalCase if exported, camelCase if package-private.
- Use typed constants (const StatusOK = 200) for API clarity.
- Prefer const over var for values that never change.
- Document iota blocks with a comment on the first line for go doc.
- Use parentheses in complex expressions for readability.
Common mistakes
- Assuming iota continues across separate const blocks — it resets.
- Using == to compare slices — compares pointers, not contents.
- Integer division surprise: 5/2 is 2, not 2.5 — use float64 conversion.
- Modifying const — compile error, but trying via pointer tricks fails.
- Confusing & (address) with & (bitwise AND) depending on context.
Advanced interview questions
Q1BeginnerWhat is iota?
Q2BeginnerShort-circuit evaluation?
Q3IntermediateTyped vs untyped constants?
Q4IntermediateCompare two slices for equality?
Q5AdvancedDesign permission system with iota bit flags.
Summary
const and iota create clean enumerations and bit flags. Operators require matching types; conversions are explicit. Short-circuit && and || enable safe nil-check chains. Never use == for slice content comparison — use slices.Equal. Next lesson: formatted I/O with fmt, bufio, and os packages.