59

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?

Matt Joiner
  • 112,946
  • 110
  • 377
  • 526

9 Answers9

59

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.

Wolf
  • 9,679
  • 7
  • 62
  • 108
zzzz
  • 87,403
  • 16
  • 175
  • 139
  • 3
    or return a `*string` from `a()` – thwd May 10 '12 at 18:05
  • 7
    you can also take the address of a composite literal, which doesn't have a "home" – newacct May 10 '12 at 22:53
  • 1
    Composite literal doesn't necessarily have a home, but the specs guarantee that a composite literal which address is taken by the `&` operator has a "home" (no other option possible anyway, otherwise there would be no address to take). – zzzz May 11 '12 at 07:11
  • 9
    all this home and homeless gibberish. is there more intuitive nomenclature than rvalue and lvalue? – Matt Joiner Jul 20 '15 at 13:16
  • I made a little package for this since one ends up doing this a lot if populating structs with a lot of pointer properties: https://github.com/byrnedo/apibase/blob/master/helpers/pointerhelp/pointerhelp.go – byrnedo May 29 '17 at 10:47
16

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.

newacct
  • 119,665
  • 29
  • 163
  • 224
  • If a function returns a non-pointer value, it seems clear to me that its assumed the return value is stored on the stack. But if you immediately take the address of the return value, I don't see any ambiguity of promoting storage for the return onto the heap, as per normal? Hell, since the caller frame hasn't had any opportunity to make use of the return value yet, the caller could easily copy the result directly into the heap upon return. – Matt Joiner May 11 '12 at 10:02
5

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.

  • 1
    Hm this actually makes a lot of sense. I overlooked the fact that the function call is an expression. – Matt Joiner May 11 '12 at 12:44
  • The error message appears to be pretty clear about how Go interprets &(i + j), but maybe not. It'd be reasonable (maybe not in Go) for a programmer to interpret this as meaning "the address of the value resulting from the execution of the expression (i + j)", and some actually are interpreting it like that. (I've no opinion as to whether Go is right or wrong... but is 'a' an expression? &a works for it, '&(a)' works too :-) So, is Go objecting to an expression or a stack allocated value (and simply calling it by the name "i + j")? Does it matter? – hutch May 11 '12 at 15:37
  • 1
    @hutch Go (at least in the current implementation) is objecting to the expression. From programmer viewpoint it doesn't matter how the compiler is doing it. –  May 11 '12 at 17:52
1

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.

hutch
  • 326
  • 1
  • 8
1

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

1

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"))
andrewdotn
  • 32,721
  • 10
  • 101
  • 130
0

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.

Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
0

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

nz_21
  • 6,140
  • 7
  • 34
  • 80
-1

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.

Izana
  • 2,537
  • 27
  • 33
  • I think it's not helpful to show that with an C++ example because C++ and golang are two totally different programming languages with complete different syntax rules. – Sebi2020 Apr 14 '20 at 12:15
  • well, they share a lot of concepts...google's motivation to design go is driven by the fact that C++ is quite difficult to use...Since google heavily uses C++, the design of Go is also affected by C++ syntax. Ironically, google uses Go in very few internal scenarios. – Izana Apr 14 '20 at 15:40