Taking address of string literal

14,944 views
Skip to first unread message

yuku

unread,
Nov 25, 2009, 4:01:35 AM11/25/09
to golang-nuts
I wonder why taking address of a string literal is not allowed.

For example, &"go is great" is not allowed.

If it were allowed, we could make a function that emulates nullable
string, for example:

func indent(n int, pad *string) {
if pad == nil { pad = "\t" }
for i := 0; i < n; i++ { fmt.Printf(pad) }
}

indent(2, nil); // prints "\t\t", nil acts as "default value", because
we don't have function overloading
indent(2, &""); // prints "", for some cases where needed
indent(2, &" "); // prints " ", spaces instead of tabs

From what I understand, strings are immutable, so string literals must
be stored somewhere in the memory, so we should be able
to get the memory address.

While &StructType{...} is allowed, why address-of string literals are
not allowed?
Message has been deleted

adam_smith

unread,
Nov 25, 2009, 4:14:18 AM11/25/09
to golang-nuts
Because StrucType instances are not immutable. I don't see why you
couldn't use a string slice or pointer to a byte array. Byte arrays
allow you to essentially have mutable strings.

yuk...@gmail.com

unread,
Nov 25, 2009, 4:43:49 AM11/25/09
to golang-nuts
My point is not to have mutable strings, but to have a parameter that accepts either nil or string.

I just feel weird that we can take an address of a literal struct (which is mutable, but that does not matter, IMHO),
but not a literal string.

See the comments in the following code:

type S struct {
A int;
B int;
}

func modify1(s *S) {
*s = S{3, 4};
}

func modify2(s *string) {
   *s = "34"; // this is allowed
   // *s[1] = 9; // this is not allowed, that's OK
}

t := S{1, 2}; 
modify1(&t); // this is allowed
print(s.A, s.B); // prints "3" "4"

s := "12";
modify2(&s); // this is allowed (1)
print(s) // prints "34"

modify1(&S{1, 2}); // this is allowed (2), the S{1,2} literal is gone

modify2(&"12") // but this is not allowed (3), but I think should be allowed


Point (1) and (2) seems to contradict with (3).



Robert Griesemer

unread,
Nov 25, 2009, 1:51:35 PM11/25/09
to yuku, golang-nuts
Because strings are immutable. If you could take the address of a string constant, you could call a function

func changeStringLiteral(s *string) {
   *s = "bar"
}

with changeStringLiteral(&"foo") with possibly strange effects - you certainly wouldn't want the literal string constant to change. One could say that whenever one takes the address of a string literal, a copy is made implicitly. But that is not how the language is defined and you can do it trivially yourself:

copy := "foo";
changeStringLiteral(&copy);

- gri

yuk...@gmail.com

unread,
Nov 25, 2009, 2:14:15 PM11/25/09
to Robert Griesemer, golang-nuts
Thanks for replying.

I don't really understand why that string is immutable affects whether we can take a string literal's address.

An example is written on my other post http://groups.google.com/group/golang-nuts/msg/690b28b57d288d52

To a func that accepts (*S) where S is a struct type, we can pass &S{...} into it, and whatever happens to that struct literal will disappear.

But to a func that accepts (*string), we cannot pass &"..." by assuming whatever can happen to that string literal, even assigning the pointer to another string (which has no effect to the caller). Modification by indexing etc is still prohibited.

Yes, we can have a temp variable like the one you mentioned, but isn't it unnecessary to write?

roger peppe

unread,
Nov 25, 2009, 2:44:46 PM11/25/09
to Robert Griesemer, golang-nuts
2009/11/25 Robert Griesemer <g...@golang.org>:
> Because strings are immutable. If you could take the address of a string

i have to say that in my naive view of the language semantics, i'd have
thought that

x := &"hello";

would be equivalent to the (legal):

x := new(string);
*x = "hello";

just as it is for struct literals.

roger peppe

unread,
Nov 25, 2009, 3:23:08 PM11/25/09
to Robert Griesemer, golang-nuts
2009/11/25 roger peppe <rogp...@gmail.com>:
>       x := &"hello";

i suppose to take that to its logical conclusion,
one should be also able to do:

x := &int(7);

and get a pointer to a heap allocated int containing the
value 7.

hmm. one part of me says "why not?" and the other
isn't at all sure.

Robert Griesemer

unread,
Nov 25, 2009, 3:31:24 PM11/25/09
to yuk...@gmail.com, golang-nuts
On Wed, Nov 25, 2009 at 11:14 AM, yuk...@gmail.com <yuk...@gmail.com> wrote:
Thanks for replying.

I don't really understand why that string is immutable affects whether we can take a string literal's address. 

An example is written on my other post http://groups.google.com/group/golang-nuts/msg/690b28b57d288d52

To a func that accepts (*S) where S is a struct type, we can pass &S{...} into it, and whatever happens to that struct literal will disappear.  

But to a func that accepts (*string), we cannot pass &"..." by assuming whatever can happen to that string literal, even assigning the pointer to another string (which has no effect to the caller). Modification by indexing etc is still prohibited.

It may seem strange that you cannot take the address of a string literal, but if you think of it as a constant it makes more sense. Otherwise, for consistency one would also have to allow taking the address of other constants, say &42, or &3.14, or &false, as has been pointed out in the meantime. Again, not unreasonable, but not how it is defined. The spec does not allow taking the address of constants, and a string literal is a constant (perhaps this needs to be more explicit in the spec).

A composite literals such as &S{...} where S is a composite type is not a constant. In general, a composite literal is constructed at run-time and allocated on the heap, and they represent "variables". Constants are not variables. It is a natural and cheap to take the address of a variable; it is less natural and more expensive to take the address of a constant. For a systems language, it seems that this is the right trade-off (to us).

(Assume for a moment that one _could_ take the address of constants: For the implementation that would mean that &constant would have to either a) allocate the constant in some read-only memory segment so that it cannot be modified; or b), it would have to be copied on the heap when an address is taken. a) has the undesirable property of possibly causing runtime exceptions and possibly making the garbage collector more complicated. b) requires extra work for a case that is very uncommon. Then there implications on the type system: constants are untyped. Should *constant remain untyped? Should it have a type? One could use similar rules as we have now, so &3 has *int type, etc. Finally, there may be other implications that we haven't seen yet because we have not implemented it. Again, given the marginal benefit, the current definition seems reasonable, and it's always trivial to do a manual copy in those rare cases).

- gri


- gri

Marcin 'Qrczak' Kowalczyk

unread,
Nov 25, 2009, 3:43:19 PM11/25/09
to roger peppe, Robert Griesemer, golang-nuts
2009/11/25 roger peppe <rogp...@gmail.com>:

> i suppose to take that to its logical conclusion,
> one should be also able to do:
>
>        x := &int(7);
>
> and get a pointer to a heap allocated int containing the
> value 7.
>
> hmm. one part of me says "why not?" and the other
> isn't at all sure.

The problem is with the poor choice of the &T{...} syntax. The &
operator is not clear whether a new object is created or a pointer to
an existing object is returned. It returns a pointer to an existing
object if its argument denotes an object which obviously existed
before the expression started evaluation (e.g. a variable or a field
of an object), and it creates a new object if its argument obviously
describes contents of an object by listing its fields.

Since the two meanings use the same syntax, it is hard to extend it to
expressions which neither suggest an existing object nor a new object.

There are actually three conceptually separate cases:
- We always want a new object.
- We want the address of an existing object.
- We don't care when the object is created; this can only apply to
immutable values. The compiler can allocate the object statically if
its contents are always the same, and it can coalesce objects with the
same contents.

Since Go has poor support for immutable objects, the third case rarely
applies. We could have for example "new x" where x is any expression
of type T, returning T*, and "&x" which is valid only where x is an
lvalue.

--
Marcin Kowalczyk

kyle

unread,
Nov 25, 2009, 3:46:15 PM11/25/09
to golang-nuts


On Nov 25, 11:14 am, "yuk...@gmail.com" <yuk...@gmail.com> wrote:
> Thanks for replying.
>
> I don't really understand why that string is immutable affects whether we
> can take a string literal's address.
>
> An example is written on my other posthttp://groups.google.com/group/golang-nuts/msg/690b28b57d288d52
>
> To a func that accepts (*S) where S is a struct type, we can pass &S{...}
> into it, and whatever happens to that struct literal will disappear.
>
> But to a func that accepts (*string), we cannot pass &"..." by assuming
> whatever can happen to that string literal, even assigning the pointer to
> another string (which has no effect to the caller). Modification by indexing
> etc is still prohibited.
>
> Yes, we can have a temp variable like the one you mentioned, but isn't it
> unnecessary to write?

I had a program in which I wanted to return pointers to string
literals rather often, and I just used:

func ptrTo(s string) *string {
return &s;
}

and found that to be much more clear and convenient than the make-a-
variable-and-return-its-address idiom.

Robert Griesemer

unread,
Nov 25, 2009, 3:55:10 PM11/25/09
to Marcin 'Qrczak' Kowalczyk, roger peppe, golang-nuts
On Wed, Nov 25, 2009 at 12:43 PM, Marcin 'Qrczak' Kowalczyk <qrcz...@gmail.com> wrote:
2009/11/25 roger peppe <rogp...@gmail.com>:

> i suppose to take that to its logical conclusion,
> one should be also able to do:
>
>        x := &int(7);
>
> and get a pointer to a heap allocated int containing the
> value 7.
>
> hmm. one part of me says "why not?" and the other
> isn't at all sure.

The problem is with the poor choice of the &T{...} syntax. The &
operator is not clear whether a new object is created or a pointer to
an existing object is returned. It returns a pointer to an existing

& *always* returns the address of an *existing* and *addressable* operand. Constants are not addressable for the reasons mentioned before.
 
object if its argument denotes an object which obviously existed
before the expression started evaluation (e.g. a variable or a field
of an object), and it creates a new object if its argument obviously
describes contents of an object by listing its fields.

T{...} *always* creates a new object that is addressable.

- gri

Marcin 'Qrczak' Kowalczyk

unread,
Nov 25, 2009, 3:59:42 PM11/25/09
to Robert Griesemer, roger peppe, golang-nuts
2009/11/25 Robert Griesemer <g...@golang.org>:

> & *always* returns the address of an *existing* and *addressable* operand.

> T{...} *always* creates a new object that is addressable.

So what is lacking is a syntax for creating a new addressable object
initialized differently than by listing its fields, so & can be
applied to it.

--
Marcin Kowalczyk

Robert Griesemer

unread,
Nov 25, 2009, 5:00:04 PM11/25/09
to Marcin 'Qrczak' Kowalczyk, roger peppe, golang-nuts
In the past, we have discussed allowing the T{} notation for types T that are not composite types. For instance, one could allow int{3} so that one could write &int{3}. But since you can trivially write a function that does that, there is no need for special language support.

func intAddr(x int) *int {
   return &x;
}

intAddr(3) gives the desired result.

You can also turn it around: The reason for the T{} notation is not primarily that we can take the address of it - albeit that is very convenient. The purpose of T{} is so that one can write literals of type T in the first place (one can write literals of non-composite types trivially (42, "foo", false, 3.14)). Even if one could not take the address of a T{}, one could use a helper function as illustrated above to get the address.

So the question might be: Why can we take the address of a T{} in the first place? Such T's are usually created at run-time (in contrast to constants), are allocated in memory (in contrast to constants, with the exception of strings), and taking the address is not only "natural" but also cheap. Not being able to take the address can be costly because it may require a copy. Also such T's are "variables", while constants are not.

A smart compiler may be able to detect when a copy is being made only so that one can take the address of a value and optimize it away and then it could all be consistent the way you'd like it. But at the end of the day even consistency is a matter of viewpoint and thus a design decision, and as pointed out before, the current situation seemed a natural decision for a systems language.

- gri

yuk...@gmail.com

unread,
Nov 26, 2009, 4:28:32 AM11/26/09
to Robert Griesemer, golang-nuts
On Thu, Nov 26, 2009 at 04:31, Robert Griesemer <g...@golang.org> wrote:
(Assume for a moment that one _could_ take the address of constants: For the implementation that would mean that &constant would have to either a) allocate the constant in some read-only memory segment so that it cannot be modified; or b), it would have to be copied on the heap when an address is taken. a) has the undesirable property of possibly causing runtime exceptions and possibly making the garbage collector more complicated. b) requires extra work for a case that is very uncommon. Then there implications on the type system: constants are untyped. Should *constant remain untyped? Should it have a type? One could use similar rules as we have now, so &3 has *int type, etc. Finally, there may be other implications that we haven't seen yet because we have not implemented it. Again, given the marginal benefit, the current definition seems reasonable, and it's always trivial to do a manual copy in those rare cases).

My purpose of taking the address of a string literal is to elegantly support null strings. So, 

I guess when we take the address of a string variable "abc" -- let's say the returned address is 0x1234 -- the character 'a' is not located at 0x1234, but 0x1234 is an address to a struct that has string length and *another pointer* to the byte array. If that is correct, technically we can let nil as a string value, by setting the *another pointer* to null.

allowing nil value for strings has better compatibility when dealing with other languages.

So how about allowing nil as a string, then we don't need to have &"string" anymore.
 
Reply all
Reply to author
Forward
0 new messages