Skip to content

Instantly share code, notes, and snippets.

@danhodge
Last active November 12, 2025 20:34
Show Gist options
  • Select an option

  • Save danhodge/f234a6baf9da695c9ddd17f831d9dc2a to your computer and use it in GitHub Desktop.

Select an option

Save danhodge/f234a6baf9da695c9ddd17f831d9dc2a to your computer and use it in GitHub Desktop.

Revisions

  1. danhodge revised this gist Nov 12, 2025. 1 changed file with 22 additions and 0 deletions.
    22 changes: 22 additions & 0 deletions go.go
    Original file line number Diff line number Diff line change
    @@ -105,6 +105,28 @@ fn = TakesIntReturnsString
    TakesDoer(fn, 8)


    // implement a function with an interface

    // 1. given some interface
    type MyInterface interface {
    DoIt(string) (string, error)
    // many other functions
    }

    // 2. define a function type for one of the functions

    type MyFunc func(string) (string, error)

    // 3. create a instance of the interface

    thing := MyThing{} // implements MyInterface

    // 4. access the method

    var fn MyFunc
    fn = thing.DoIt


    // implement a function that captures state without using a struct

    type Searcher func(q string) string
  2. danhodge revised this gist Oct 22, 2025. No changes.
  3. danhodge revised this gist Oct 22, 2025. 1 changed file with 12 additions and 0 deletions.
    12 changes: 12 additions & 0 deletions go.go
    Original file line number Diff line number Diff line change
    @@ -104,6 +104,18 @@ fn = TakesIntReturnsString
    // 5. Pass it into the function from step 3
    TakesDoer(fn, 8)


    // implement a function that captures state without using a struct

    type Searcher func(q string) string

    func NewSearcher(client *search.client) Searcher {
    return func(q string) string {
    return client.SearchQuery(q)
    }
    }


    // Shorthand for passing an error (or any other local variable) into a deferred function

    func somefunc() (_ int, err error) { // use an _ for the unnamed return value - all values have to be named when using named values
  4. danhodge revised this gist Oct 2, 2025. 1 changed file with 20 additions and 0 deletions.
    20 changes: 20 additions & 0 deletions go.go
    Original file line number Diff line number Diff line change
    @@ -118,6 +118,26 @@ func somefunc() (_ int, err error) { // use an _ for the unnamed return value -
    // do stuff that may or may not result in err being set
    }

    // the only way to modify a return value from a deferred function is using named parameters

    // returns 0
    func f() int {
    res := 0
    defer func(){
    res++
    }()
    return res
    }

    // returns 1
    func f() (res int) {
    res = 0
    defer func(){
    res++
    }()
    return res
    }

    // struct embedding + methods & "inheritance"

    type Base struct {
  5. danhodge revised this gist Sep 24, 2025. 1 changed file with 20 additions and 0 deletions.
    20 changes: 20 additions & 0 deletions go.go
    Original file line number Diff line number Diff line change
    @@ -156,6 +156,8 @@ func main() {
    }

    // Check to see if an error chain includes a particular error
    // Note: the type of p needs to correspond to the receiver type for the Error() method on SomeErrorType
    // meaning, if Error() has a pointer receiver on SomeErrorType, p needs to be a pointer, otherwise, it needs to be a value

    var p *SomeErrorType
    errors.As(err, &p)
    @@ -578,3 +580,21 @@ func (m *MyThing) Produce(msg string) error {
    }
    return nil
    }

    // Method Sets
    // from: https://stackoverflow.com/a/33591156
    /*
    1. If you have a *T you can call methods that have a receiver type of *T as well as methods that have a receiver type of T.
    2. If you have a T and it is addressable you can call methods that have a receiver type of *T as well as methods that have
    a receiver type of T, because the method call t.Meth() will be equivalent to (&t).Meth() (Calls).
    3. If you have a T and it isn't addressable (for instance, the result of a function call, or the result of indexing into a
    map), Go can't get a pointer to it, so you can only call methods that have a receiver type of T, not *T.
    4. If you have an interface I, and some or all of the methods in I's method set are provided by methods with a receiver of
    *T (with the remainder being provided by methods with a receiver of T), then *T satisfies the interface I, but T doesn't.
    That is because *T's method set includes T's, but not the other way around (back to the first point again).
    In short, you can mix and match methods with value receivers and methods with pointer receivers, and use them with variables
    containing values and pointers, without worrying about which is which. Both will work, and the syntax is the same. However,
    if methods with pointer receivers are needed to satisfy an interface, then only a pointer will be assignable to the interface
    — a value won't be valid.
    */
  6. danhodge revised this gist Sep 12, 2025. 1 changed file with 23 additions and 1 deletion.
    24 changes: 23 additions & 1 deletion go.go
    Original file line number Diff line number Diff line change
    @@ -555,4 +555,26 @@ func main() {

    v := <-ch
    fmt.Printf("r = %d, err = %v\n", v.r, v.err)
    }
    }

    // how to override a method in a test without using interfaces or mocks - somewhat contrived example

    // some external dependency that defines the method named "Produce" that needs to be mocked
    type ExternalDependency struct {
    }

    // my_code.go
    type MyThing {
    ExternalDependency
    }

    // my_code_test.go
    var fakeProducer func(string) error

    // tests will use this version of the Produce function, which exposes a hook function for spying/mocking
    func (m *MyThing) Produce(msg string) error {
    if fakeProducer != nil {
    return fakeProducer(msg)
    }
    return nil
    }
  7. danhodge revised this gist Jun 2, 2025. 1 changed file with 24 additions and 1 deletion.
    25 changes: 24 additions & 1 deletion go.go
    Original file line number Diff line number Diff line change
    @@ -532,4 +532,27 @@ func TakesIdentifiable[T Identifiable](seq iter.Seq[T]) {
    fmt.Printf(val)
    }
    }
    TakesIdentifiable(slices.Values([]ValueThing{ValueThing{Id: "123"}, ValueThing{Id: "456"}}))
    TakesIdentifiable(slices.Values([]ValueThing{ValueThing{Id: "123"}, ValueThing{Id: "456"}}))

    // "return" a value, err from a goroutine

    func DoStuff() (int, error) {
    return 12, errors.New("error")
    }

    func main() {
    ch := make(chan struct {
    r int
    err error
    }, 1)
    go func() {
    r, err := DoStuff()
    ch <- struct {
    r int
    err error
    }{r, err}
    }()

    v := <-ch
    fmt.Printf("r = %d, err = %v\n", v.r, v.err)
    }
  8. danhodge revised this gist Apr 4, 2025. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion go.go
    Original file line number Diff line number Diff line change
    @@ -526,7 +526,7 @@ TakesIdentifiable(&pt)
    // Go does not support covariance on slices, so this doesn't compile
    var slc = []Identifable{ValueThing{Id: "123"}, ValueThing{Id: "456"}}

    // range over func can be used to pass an array concrete type as a parameter to a function that takes an interface type
    // range over func can be used to pass an slice of concrete types as a parameter to a function that takes an interface type
    func TakesIdentifiable[T Identifiable](seq iter.Seq[T]) {
    for val := range seq {
    fmt.Printf(val)
  9. danhodge revised this gist Apr 4, 2025. 1 changed file with 9 additions and 2 deletions.
    11 changes: 9 additions & 2 deletions go.go
    Original file line number Diff line number Diff line change
    @@ -523,6 +523,13 @@ TakesIdentifiable(pt)
    // compiles since the argument is now a *PointerThing, which does satisfy the Identifiable interface
    TakesIdentifiable(&pt)

    // Go does not support covariance on slices, so this doesn't compile
    var slc = []Identifable{ValueThing{Id: "123"}, ValueThing{Id: "456"}}



    // range over func can be used to pass an array concrete type as a parameter to a function that takes an interface type
    func TakesIdentifiable[T Identifiable](seq iter.Seq[T]) {
    for val := range seq {
    fmt.Printf(val)
    }
    }
    TakesIdentifiable(slices.Values([]ValueThing{ValueThing{Id: "123"}, ValueThing{Id: "456"}}))
  10. danhodge revised this gist Apr 4, 2025. 1 changed file with 36 additions and 0 deletions.
    36 changes: 36 additions & 0 deletions go.go
    Original file line number Diff line number Diff line change
    @@ -488,5 +488,41 @@ fmt.Printf("%v\n", s) // prints []
    sub := s[0:2]
    fmt.Printf("%v\n", sub) // prints [A B]

    // interface satisfaction

    // given this interface
    type Identifiable interface {
    GetId() string
    }

    // and these implementations
    type ValueThing struct {
    Id string
    }

    func (t ValueThing) GetId() string {
    return t.Id
    }

    type PointerThing struct {
    Id string
    }

    func (t *PointerThing) GetId() string {
    return t.Id
    }

    vt := ValueThing{Id: "123"}
    pt := PointerThing{Id: "456"}

    // compiles
    TakesIdentifiable(vt)
    // does not compile because PointerThing does not implement the Identifiable, *PointerThing does (due to pointer receiver)
    TakesIdentifiable(pt)

    // compiles since the argument is now a *PointerThing, which does satisfy the Identifiable interface
    TakesIdentifiable(&pt)




  11. danhodge revised this gist Mar 26, 2025. 1 changed file with 15 additions and 0 deletions.
    15 changes: 15 additions & 0 deletions go.go
    Original file line number Diff line number Diff line change
    @@ -473,5 +473,20 @@ s := []string{}
    var m map[string]string
    var s []string{}

    // clearing a slice

    var s []string
    s = append(s, "A")
    s = append(s, "B")
    s = append(s, "C")

    // this clears the slice by setting len = 0 but it doesn't remove any of the elements
    s = s[:0]
    fmt.Printf("%v\n", s) // prints []

    // you can still create a subslice from s up to the slice's existing capacity, which will include the existing elements
    sub := s[0:2]
    fmt.Printf("%v\n", sub) // prints [A B]



  12. danhodge revised this gist Mar 7, 2025. 1 changed file with 13 additions and 0 deletions.
    13 changes: 13 additions & 0 deletions go.go
    Original file line number Diff line number Diff line change
    @@ -462,3 +462,16 @@ func main() {
    fmt.Printf("t2 = %v\n", t2)
    }

    // initializing slices & maps

    // these both allocate empty data structures that can have keys/values added to them
    m := map[string]string{}
    s := []string{}

    // these don't allocate any storage and are nil
    // nil slices can be appended to using the append function but nil maps can't accept keys/values
    var m map[string]string
    var s []string{}



  13. danhodge revised this gist Jan 20, 2025. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion go.go
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    // types and literals

    // defines a new type (note - this is not a type alias, type aliases are a different thing, defined by type ALIAS = ORIGNAL)
    // defines a new type (note - this is not a type alias, type aliases are a different thing, defined by: type ALIAS = ORIGNAL)
    type Thing int32

    // this function only accepts arguments of type Thing, int32 arguments are not allowed by the compiler
  14. danhodge revised this gist Jan 20, 2025. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion go.go
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    // types and literals

    // defines a new type (note - this is not a type alias, type aliases are a different thing)
    // defines a new type (note - this is not a type alias, type aliases are a different thing, defined by type ALIAS = ORIGNAL)
    type Thing int32

    // this function only accepts arguments of type Thing, int32 arguments are not allowed by the compiler
  15. danhodge revised this gist Nov 13, 2024. 1 changed file with 31 additions and 0 deletions.
    31 changes: 31 additions & 0 deletions go.go
    Original file line number Diff line number Diff line change
    @@ -431,3 +431,34 @@ payload = `{"identifier": "1234"}`
    type Thing struct {
    Id int `json:identifier,string`
    }

    // Closures are able to capture local variables

    type Thing struct {
    Val string
    }

    func Run(fn func()) {
    fn()
    }

    func NewThing(v string) Thing {
    return Thing{Val: v}
    }

    func main() {
    t1 := Thing{Val: "123"}
    Run(func() {
    // can modify locals from a closure
    t1.Val = "456"
    })
    fmt.Printf("t1 = %v\n", t1)

    t2 := Thing{}
    Run(func() {
    // can reassign locals from a closure
    t2 = NewThing("789")
    })
    fmt.Printf("t2 = %v\n", t2)
    }

  16. danhodge revised this gist Oct 22, 2024. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion go.go
    Original file line number Diff line number Diff line change
    @@ -236,7 +236,8 @@ type structName struct {}
    // fails to compile due to no: func (s *structName) Func1(s string) string { return "val" }
    // what is this doing?
    // declares a variable of type Interface name (that we don't care about so it's assigned to _)
    // and tries assigning a variable of type pointer to structName
    // and tries assigning a variable of type pointer to structName to it and the assignment will fail
    // if structName does not implement all of the methods defined on InterfaceName
    // - uses nil because nil can be assigned to any pointer so no need to declare a structName variable
    // - uses a pointer because it is idiomatic to use pointer receivers when defining methods, if you could
    // guarantee that structName would never need to use pointer receivers, you could achieve the same thing
  17. danhodge revised this gist Oct 22, 2024. 1 changed file with 8 additions and 1 deletion.
    9 changes: 8 additions & 1 deletion go.go
    Original file line number Diff line number Diff line change
    @@ -231,9 +231,16 @@ type InterfaceName {
    Func1(s string) string
    }

    type structName {}
    type structName struct {}

    // fails to compile due to no: func (s *structName) Func1(s string) string { return "val" }
    // what is this doing?
    // declares a variable of type Interface name (that we don't care about so it's assigned to _)
    // and tries assigning a variable of type pointer to structName
    // - uses nil because nil can be assigned to any pointer so no need to declare a structName variable
    // - uses a pointer because it is idiomatic to use pointer receivers when defining methods, if you could
    // guarantee that structName would never need to use pointer receivers, you could achieve the same thing
    // by assinging a zero value of structName to an InterfaceName variable.
    var _ InterfaceName = (*structName)(nil)

    // JSON marshaling omit attributes if not set
  18. danhodge revised this gist Oct 18, 2024. 1 changed file with 8 additions and 0 deletions.
    8 changes: 8 additions & 0 deletions go.go
    Original file line number Diff line number Diff line change
    @@ -415,3 +415,11 @@ func main() {
    fmt.Printf("Result: %s\n", s)
    }
    }

    // Deserialize JSON string as int

    payload = `{"identifier": "1234"}`

    type Thing struct {
    Id int `json:identifier,string`
    }
  19. danhodge revised this gist Oct 18, 2024. 1 changed file with 31 additions and 1 deletion.
    32 changes: 31 additions & 1 deletion go.go
    Original file line number Diff line number Diff line change
    @@ -384,4 +384,34 @@ func main() {
    }

    // print full struct info (FQN & field names/values)
    fmt.Printf("%#v", val)
    fmt.Printf("%#v", val)

    // channels and go routines
    // https://go.dev/play/p/SwLwKziIb_A

    func work(num int) string {
    return fmt.Sprintf("Job: %d", num)
    }

    func main() {
    ct := 10
    ch := make(chan string, ct)

    var wg sync.WaitGroup

    for i := range ct {
    wg.Add(1)
    go func() error {
    defer wg.Done()
    ch <- work(i)
    return nil
    }()
    }

    wg.Wait() // wait for all goroutines to finish
    close(ch) // close the results channel (otherwise, range loop will never terminate)

    for s := range ch {
    fmt.Printf("Result: %s\n", s)
    }
    }
  20. danhodge revised this gist Jul 2, 2024. 1 changed file with 8 additions and 0 deletions.
    8 changes: 8 additions & 0 deletions go.go
    Original file line number Diff line number Diff line change
    @@ -236,6 +236,14 @@ type structName {}
    // fails to compile due to no: func (s *structName) Func1(s string) string { return "val" }
    var _ InterfaceName = (*structName)(nil)

    // JSON marshaling omit attributes if not set
    type Thing struct {
    Value1 string `json:"value1"` // marshals "value1": "" if not set
    Value2 string `json:"value2,omitempty"` // omits "value1" if not set or set to ""
    Value3 *string `json:"value3"` // marshals "value1": null if not set
    Value4 *string `json:"value4,omitempty"` // omits "value1" if not set or set to nil
    }


    // Custom JSON unmarshaling with validation: https://go.dev/play/p/jYPARNjmhVn

  21. danhodge revised this gist Jun 3, 2024. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions go.go
    Original file line number Diff line number Diff line change
    @@ -304,11 +304,13 @@ type Descriptor struct {
    }

    func (a *Descriptor) UnmarshalJSON(data []byte) error {
    // need to use a different type definition here (desc2 instead of Descriptor) to avoid infinite recursion during unmarshaling
    type desc2 Descriptor
    tmp := struct {
    BaseURL string `json:"baseUrl"`
    *desc2
    }{
    // embeds a pointer to the receiver in this anonymous struct to receive the other attributes as is
    desc2: (*desc2)(a),
    }

  22. danhodge revised this gist May 20, 2024. 1 changed file with 34 additions and 0 deletions.
    34 changes: 34 additions & 0 deletions go.go
    Original file line number Diff line number Diff line change
    @@ -293,6 +293,40 @@ func main() {
    }
    }

    // custom JSON unmarshaling using an embedded struct to separate custom & default parsing
    // https://ukiahsmith.com/blog/improved-golang-unmarshal-json-with-time-and-url/

    type Descriptor struct {
    Name string `json:"name"`
    Key string `json:"key"`
    Description string `json:"description"`
    BaseURL url.URL `json:"baseUrl"`
    }

    func (a *Descriptor) UnmarshalJSON(data []byte) error {
    type desc2 Descriptor
    tmp := struct {
    BaseURL string `json:"baseUrl"`
    *desc2
    }{
    desc2: (*desc2)(a),
    }

    err := json.Unmarshal(data, &tmp)
    if err != nil {
    return err
    }

    baseURL, err := url.Parse(tmp.BaseURL)
    if err != nil {
    return err
    }
    a.BaseURL = *baseURL

    return nil
    }


    // embedding an interface in a struct can be used for delegation
    // https://go.dev/play/p/xnmxLdCXjqL

  23. danhodge revised this gist May 10, 2024. 1 changed file with 4 additions and 1 deletion.
    5 changes: 4 additions & 1 deletion go.go
    Original file line number Diff line number Diff line change
    @@ -337,4 +337,7 @@ func main() {

    wt := WrappedThing(t, 17)
    fmt.Printf("%d, %d\n", wt.Increase(n), wt.Decrease(n))
    }
    }

    // print full struct info (FQN & field names/values)
    fmt.Printf("%#v", val)
  24. danhodge revised this gist May 1, 2024. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions go.go
    Original file line number Diff line number Diff line change
    @@ -294,6 +294,7 @@ func main() {
    }

    // embedding an interface in a struct can be used for delegation
    // https://go.dev/play/p/xnmxLdCXjqL

    // the common interface
    type Thing interface {
  25. danhodge revised this gist May 1, 2024. 1 changed file with 44 additions and 0 deletions.
    44 changes: 44 additions & 0 deletions go.go
    Original file line number Diff line number Diff line change
    @@ -293,3 +293,47 @@ func main() {
    }
    }

    // embedding an interface in a struct can be used for delegation

    // the common interface
    type Thing interface {
    Increase(i int32) int32
    Decrease(i int32) int32
    }

    // the base implementation
    type thing struct{}

    func (t thing) Increase(i int32) int32 {
    return i + 10
    }

    func (t thing) Decrease(i int32) int32 {
    return i - 10
    }

    // the wrapped implementation (embeds the common interface)
    type Wrapped struct {
    Thing
    Amount int32
    }

    // changes the Increase method to delegate to the base
    // implementation and then do something else
    func (w Wrapped) Increase(i int32) int32 {
    return w.Thing.Increase(i) + w.Amount
    }

    // wraps a base implementation in the wrapped implementation
    func WrappedThing(thing Thing, amt int32) Thing {
    return &Wrapped{thing, amt}
    }

    func main() {
    var n int32 = 20
    t := thing{}
    fmt.Printf("%d, %d\n", t.Increase(n), t.Decrease(n))

    wt := WrappedThing(t, 17)
    fmt.Printf("%d, %d\n", wt.Increase(n), wt.Decrease(n))
    }
  26. danhodge revised this gist Mar 26, 2024. 1 changed file with 59 additions and 1 deletion.
    60 changes: 59 additions & 1 deletion go.go
    Original file line number Diff line number Diff line change
    @@ -234,4 +234,62 @@ type InterfaceName {
    type structName {}

    // fails to compile due to no: func (s *structName) Func1(s string) string { return "val" }
    var _ InterfaceName = (*structName)(nil)
    var _ InterfaceName = (*structName)(nil)


    // Custom JSON unmarshaling with validation: https://go.dev/play/p/jYPARNjmhVn

    type Outer struct {
    Name string `json:"name"`
    Things []Inner `json:"things"`
    }

    func (o *Outer) UnmarshalJSON(b []byte) error {
    var raw map[string]json.RawMessage
    if err := json.Unmarshal(b, &raw); err != nil {
    return err
    }

    if err := json.Unmarshal(raw["name"], &o.Name); err != nil {
    return err
    }

    if err := json.Unmarshal(raw["things"], &o.Things); err != nil {
    return err
    }

    return nil
    }

    type Inner struct {
    Value int32 `json:"val"`
    }

    func (i *Inner) UnmarshalJSON(b []byte) error {
    var raw map[string]json.RawMessage
    if err := json.Unmarshal(b, &raw); err != nil {
    return err
    }

    if err := json.Unmarshal(raw["val"], &i.Value); err != nil {
    return err
    }
    if i.Value < 100 {
    return errors.New("Too Low")
    }

    return nil
    }

    func main() {
    var x Outer
    str := `{"name": "Me", "things": [{ "val": 120 }, { "val": 30 }]}`
    err := json.Unmarshal([]byte(str), &x)
    if err != nil {
    // fails because val=30 is < 100
    fmt.Printf("Error: %v\n", err)
    } else {
    fmt.Printf("Value: %+v\n", x)
    }
    }

  27. danhodge revised this gist Feb 1, 2024. 1 changed file with 12 additions and 1 deletion.
    13 changes: 12 additions & 1 deletion go.go
    Original file line number Diff line number Diff line change
    @@ -223,4 +223,15 @@ func main() {
    } else {
    fmt.Printf("OK: %v\n", val)
    }
    }
    }

    // force a compile-time error if a struct doesn't fully implement a given interface

    type InterfaceName {
    Func1(s string) string
    }

    type structName {}

    // fails to compile due to no: func (s *structName) Func1(s string) string { return "val" }
    var _ InterfaceName = (*structName)(nil)
  28. danhodge revised this gist Oct 25, 2023. 1 changed file with 34 additions and 1 deletion.
    35 changes: 34 additions & 1 deletion go.go
    Original file line number Diff line number Diff line change
    @@ -190,4 +190,37 @@ defer cancel2()

    // the deadline for ctx1a will override the parent context's deadline because 5ms < 10ms
    ctx1a, cancel1a := context.WithDeadline(ctx1, time.Now().Add(time.Duration(5)*time.Millisecond))
    defer cancel1a()
    defer cancel1a()


    // json.NewDecoder() ignores garbage at the end of the stream

    type Thing struct {
    Name string `json:"name"`
    }

    func parse1(stream io.ReadCloser) (Thing, error) {
    t := Thing{}
    buf := new(bytes.Buffer)
    if _, err := buf.ReadFrom(stream); err != nil {
    return t, err
    }
    err := json.Unmarshal(buf.Bytes(), &t)
    return t, err
    }

    func parse2(stream io.ReadCloser) (Thing, error) {
    t := Thing{}
    err := json.NewDecoder(stream).Decode(&t)
    return t, err
    }

    func main() {
    // parse1 fails, parse2 succeeds
    val, err := parse1(io.NopCloser(strings.NewReader(`{"name":"Bob"}PLUS GARBAGE`)))
    if err != nil {
    fmt.Printf("Error happened: %v\n", err)
    } else {
    fmt.Printf("OK: %v\n", val)
    }
    }
  29. danhodge revised this gist Sep 29, 2023. No changes.
  30. danhodge revised this gist Sep 29, 2023. 1 changed file with 15 additions and 1 deletion.
    16 changes: 15 additions & 1 deletion go.go
    Original file line number Diff line number Diff line change
    @@ -176,4 +176,18 @@ func genericFunction[T any]() (T, error) {
    ctx, cancel := context.WithDeadline(ctx, time.Now().Add(time.Duration(deadline) * time.Millisecond))
    defer cancel()

    conn.Operation(ctx, ...)
    conn.Operation(ctx, ...)

    // when setting deadlines, the earliest deadline takes precedence

    root := context.Background()
    ctx1, cancel1 := context.WithDeadline(root, time.Now().Add(time.Duration(10)*time.Millisecond))
    defer cancel1()

    // the deadline for ctx2 will not override the parent context's deadline because 10ms < 10s
    ctx2, cancel2 := context.WithDeadline(ctx1, time.Now().Add(time.Duration(10)*time.Second))
    defer cancel2()

    // the deadline for ctx1a will override the parent context's deadline because 5ms < 10ms
    ctx1a, cancel1a := context.WithDeadline(ctx1, time.Now().Add(time.Duration(5)*time.Millisecond))
    defer cancel1a()