The short answer is no, this won't work reliably, for several reasons:

  1. Race between move and goroutine creation

  go func(c2 *ftp.ServerConn) {
      c2.List()
  }(move(&c))

  The sequence is:
  1. move(&c) executes in G1
  2. Return value sits in G1's registers/stack
  3. New goroutine G2 is scheduled
  4. Value is copied to G2's stack

  Between steps 1-4, the pointer lives in G1's execution context. The GC 
could run during this window.

  2. Compiler doesn't understand ownership

  c, err := getFtpConnection()
  // Compiler may keep 'c' in a register here
  go func(c2 *ftp.ServerConn) {
      c2.List()
  }(move(&c))
  // Even with c = nil, compiler might have copies

  Go's compiler can:
  - Keep copies in registers
  - Introduce temporaries you don't see
  - Reorder operations

  Setting c = nil in source doesn't guarantee all machine-level references 
are cleared.

  3. GC scans conservatively

  Go's GC scans entire stack frames, including:
  - All slots that could hold pointers
  - Registers at safepoints
  - Temporary locations

  There's no mechanism to tell the GC "I've semantically transferred this 
pointer."

  4. The move generic doesn't compile

  func move[PT *any](pp *PT) (res PT) // Won't work

  You'd need:
  func move[T any, PT *T](pp *PT) PT

  But even then, it doesn't achieve the semantic goal.

  5. What would actually be needed

  For true ownership transfer, you'd need runtime support:

  // Hypothetical - doesn't exist
  func runtime.TransferOwnership[T any](from *T) T {
      // 1. Atomically mark object as "in transfer"
      // 2. Clear stack slot with write barrier
      // 3. Return value with new ownership metadata
  }

  This would require changes to:
  - GC's stack scanner (recognize transferred objects)
  - Write barriers (track ownership changes)
  - Escape analysis (understand cross-goroutine transfers)

  The fundamental issue

  Go's memory model is shared memory with GC, not ownership-based. The GC 
assumes any reachable pointer might be used. There's no concept of "this 
goroutine owns this object."

  Rust can do this because ownership is compile-time — the borrow checker 
ensures single ownership. Go would need either:
  1. Compile-time ownership tracking (major language change)
  2. Runtime ownership metadata (significant GC overhead)

  What you can do instead

  // Use channels for explicit handoff
  ch := make(chan *ftp.ServerConn, 1)
  c, err := getFtpConnection()
  if err != nil {
      return nil, err
  }
  ch <- c
  c = nil // Now truly unreachable from G1
  go func() {
      c2 := <-ch
      c2.List()
  }()

  This achieves a cleaner ownership transfer, though the GC still manages 
the lifetime — it just now knows c in G1 is nil before G2 runs.

Em quarta-feira, 7 de janeiro de 2026 às 04:15:20 UTC-3, Axel Wagner 
escreveu:

> I'm not sure your `move` function actually does anything in this case. 
> AIUI the compiler and runtime are already clever enough to recognize that c 
> is no longer used after the `go` statement (hence the necessity of 
> runtime.KeepAlive) and that's all your `move` is trying to do, no?
> And in the general case, you have no guarantee that a pointer passed to 
> `move` is the *only* pointer to the relevant object.
> So ISTM the cases where the Go implementation *can't* tell that c is no 
> longer used, adding the `move` doesn't really help.
>
> Do you have concrete evidence that this helps in some cases? Because 
> otherwise it seems like a premature optimization that mostly makes the code 
> harder to read.
>
> On Wed, 7 Jan 2026 at 04:25, Qingwei Li <[email protected]> wrote:
>
>> Take the following program as an example.
>>
>> ```go
>> c, err := getFtpConnection()
>> if err != nil {
>> return nil, err
>> }
>> go func(c2 *ftp.ServerConn) {
>> c2.List()
>> }(c)
>> // c will not be used later
>> ```
>>
>> Let's add the `move` function.
>>
>> ```go
>> // pointer is 
>> func move[PT *any](pp *PT) (res PT) {
>> res = *pp
>> *pp = nil
>> return
>> }
>> ```
>>
>> ```go
>> // Goroutine G1
>> c, err := getFtpConnection()
>> if err != nil {
>> return nil, err
>> }
>> go func(c2 *ftp.ServerConn) { // Goroutine G2
>> c2.List()
>> }(move(&c))
>> // c will not be used later
>> ```
>>
>> Would this `move` without runtime support make GC unable to reach the 
>> object pointed by `c` scanning from the stack of goroutine G1 so that the 
>> ownership of object is entirely moved to goroutine G2? With this ownership 
>> transfering, freegc for `c2` in the end of goroutine G2 is feasible in 
>> cross-goroutine reference case.
>>
>>
>> -- 
>> You received this message because you are subscribed to the Google Groups 
>> "golang-nuts" group.
>> To unsubscribe from this group and stop receiving emails from it, send an 
>> email to [email protected].
>> To view this discussion visit 
>> https://groups.google.com/d/msgid/golang-nuts/5c0b8aaf-8470-4de2-91f6-5d74e884dff3n%40googlegroups.com
>>  
>> <https://groups.google.com/d/msgid/golang-nuts/5c0b8aaf-8470-4de2-91f6-5d74e884dff3n%40googlegroups.com?utm_medium=email&utm_source=footer>
>> .
>>
>

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/golang-nuts/17a2b878-c3a8-4572-bc73-51e8f12266cfn%40googlegroups.com.

Reply via email to