Address of a temporary in Go?

PointersReturnGoTemporaryRvalue

Pointers Problem Overview


What's the cleanest way to handle a case such as this:

func a() string {
    /* doesn't matter */
}

b *string = &a()

This generates the error:

> cannot take the address of a()

My understanding is that Go automatically promotes a local variable to the heap if its address is taken. Here it's clear that the address of the return value is to be taken. What's an idiomatic way to handle this?

Pointers Solutions


Solution 1 - Pointers

The address operator returns a pointer to something having a "home", e.g. a variable. The value of the expression in your code is "homeless". if you really need a *string, you'll have to do it in 2 steps:

tmp := a(); b := &tmp

Note that while there are completely valid use cases for *string, many times it's a mistake to use them. In Go string is a value type, but a cheap one to pass around (a pointer and an int). String's value is immutable, changing a *string changes where the "home" points to, not the string value, so in most cases *string is not needed at all.

Solution 2 - Pointers

See the relevant section of the Go language spec. & can only be used on:

  1. Something that is addressable: variable, pointer indirection, slice indexing operation, field selector of an addressable struct, array indexing operation of an addressable array; OR
  2. A composite literal

What you have is neither of those, so it doesn't work.

I'm not even sure what it would mean even if you could do it. Taking the address of the result of a function call? Usually, you pass a pointer of something to someone because you want them to be able to assign to the thing pointed to, and see the changes in the original variable. But the result of a function call is temporary; nobody else "sees" it unless you assign it to something first.

If the purpose of creating the pointer is to create something with a dynamic lifetime, similar to new() or taking the address of a composite literal, then you can assign the result of the function call to a variable and take the address of that.

Solution 3 - Pointers

In the end you are proposing that Go should allow you to take the address of any expression, for example:

i,j := 1,2
var p *int = &(i+j)
println(*p)

The current Go compiler prints the error: cannot take the address of i + j

In my opinion, allowing the programmer to take the address of any expression:

  • Doesn't seem to be very useful (that is: it seems to have very small probability of occurrence in actual Go programs).
  • It would complicate the compiler and the language spec.

It seems counterproductive to complicate the compiler and the spec for little gain.

Solution 4 - Pointers

I recently was tied up in knots about something similar.

First talking about strings in your example is a distraction, use a struct instead, re-writing it to something like:

func a() MyStruct {
    /* doesn't matter */
}

var b *MyStruct = &a()

This won't compile because you can't take the address of a(). So do this:

func a() MyStruct {
    /* doesn't matter */
}

tmpA := a()
var b *MyStruct = &tmpA

This will compile, but you've returned a MyStruct on the stack, allocated sufficient space on the heap to store a MyStruct, then copied the contents from the stack to the heap. If you want to avoid this, then write it like this:

func a2() *MyStruct {
  /* doesn't matter as long as MyStruct is created on the heap (e.g. use 'new') */
}

var a *MyStruct = a2()

Copying is normally inexpensive, but those structs might be big. Even worse when you want to modify the struct and have it 'stick' you can't be copying then modifying the copies.

Anyway, it gets all the more fun when you're using a return type of interface{}. The interface{} can be the struct or a pointer to a struct. The same copying issue comes up.

Solution 5 - Pointers

You can't get the reference of the result directly when assigning to a new variable, but you have idiomatic way to do this without the use of a temporary variable (it's useless) by simply pre-declaring your "b" pointer - this is the real step you missed:

func a() string {
	return "doesn't matter"
}

b := new(string) // b is a pointer to a blank string (the "zeroed" value)
*b = a()         // b is now a pointer to the result of `a()`

*b is used to dereference the pointer and directly access the memory area which hold your data (on the heap, of course).

Play with the code: https://play.golang.org/p/VDhycPwRjK9

Solution 6 - Pointers

Yeah, it can be annoying when APIs require the use of *string inputs even though you’ll often want to pass literal strings to them.

For this I make a very tiny function:

// Return pointer version of string
func p(s string) *string {
	return &s
}

and then instead of trying to call foo("hi") and getting the dreaded cannot use "hi" (type string) as type *string in argument to foo, I just wrap the argument in a call to to p():

foo(p("hi"))

Solution 7 - Pointers

a() doesn't point to a variable as it is on the stack. You can't point to the stack (why would you ?).

You can do that if you want

va := a()
b := &va

But what your really want to achieve is somewhat unclear.

Solution 8 - Pointers

At the time of writing this, none of the answers really explain the rationale for why this is the case.

Consider the following:


func main() {
	m := map[int]int{}
	val := 1
	m[0] = val
	v := &m[0] // won't compile, but let's assume it does 
	delete(m, 0)
	fmt.Println(v)
}

If this code snippet actually compiled, what would v point to!? It's a dangling pointer since the underlying object has been deleted.

Given this, it seems like a reasonable restriction to disallow addressing temporaries

Solution 9 - Pointers

guess you need help from More effective Cpp ;-)

Temp obj and rvalue > “True temporary objects in C++ are invisible - they don't appear in your source code. They arise whenever a non-heap object is created but not named. Such unnamed objects usually arise in one of two situations: when implicit type conversions are applied to make function calls succeed and when functions return objects.”

And from Primer Plus

> lvalue is a data object that can be referenced by address through user (named object). Non-lvalues include literal constants (aside from the quoted strings, which are represented by their addresses), expressions with multiple terms, such as (a + b).

In Go lang, string literal will be converted into StrucType object, which will be a non-addressable temp struct object. In this case, string literal cannot be referenced by address in Go.

Well, the last but not the least, one exception in go, you can take the address of the composite literal. OMG, what a mess.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMatt JoinerView Question on Stackoverflow
Solution 1 - PointerszzzzView Answer on Stackoverflow
Solution 2 - PointersnewacctView Answer on Stackoverflow
Solution 3 - Pointersuser811773View Answer on Stackoverflow
Solution 4 - PointershutchView Answer on Stackoverflow
Solution 5 - PointersLudovic De LunaView Answer on Stackoverflow
Solution 6 - PointersandrewdotnView Answer on Stackoverflow
Solution 7 - PointersDenys SéguretView Answer on Stackoverflow
Solution 8 - Pointersnz_21View Answer on Stackoverflow
Solution 9 - PointersIzanaView Answer on Stackoverflow