Sorry for the late reply and thank you all for your replies. I think I should loosen the requirements for my question without introducing the concept of ownership.
Does setting c to nil in "move" function helps GC sweep the connection object pointed by c and c2 soon if G1 keeps long but does not use connection object, G2 finishes soon and the c pointer does not exist in temporary locations, slots that can hold pointers, or registers? The program in original post <https://groups.google.com/g/golang-nuts/c/vNoKW_3joS8> in pasted here: ```go func move[T any, PT *T](pp *PT) (res PT) { res = *pp *pp = nil return } // 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 ``` I may do an experiment with goref <https://github.com/cloudwego/goref/> tool, which I recently found, and show the result here. Thanks. On Wednesday, January 7, 2026 at 8:41:56 PM UTC+8 Andrey Andrade wrote: > 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/8cb49d04-fece-4805-b2aa-fe4dd1546378n%40googlegroups.com.
