iTranslated by AI
Pointers to Literal Values
This is another small tip from:
Pointers to Literal Values
For example, in Java, you can write:
System.out.println("hello".length()); // Output: 5
Languages that claim to be "object-oriented" (not just Java) can perform this kind of feat because literal expressions are evaluated as objects. However, Go cannot do this (Note that in Go, literals are treated as untyped constants).
In the first place, the basic types that can be described with literal expressions do not have methods associated with them, so:
fmt.Println("Hello".String()) // "Hello".String undefined (type string has no field or method String)
doing this just results in being scolded with "That method doesn't exist!" (a very loose translation). Also, as shown below:
s := &"Hello" // cannot take the address of "Hello"
you cannot obtain a pointer value directly from a literal expression. By the way:
s := &string("Hello") // cannot take the address of string("Hello")
specifying the type explicitly doesn't work either. However:
s := "Hello"
fmt.Printf("%p\n", &s) // print pointer to variable
if you drop it into a variable as an instance like this, it is possible to obtain the pointer value.
Didn't you all wonder here? If you cannot take the address directly from a literal expression, why doesn't a description like this in a struct literal result in a compilation error?
type Hello struct{}
func New() *Hello {
return &Hello{}
}
Actually, I hadn't wondered about it at all until it was pointed out at this reading session. My bad orz.
This story is briefly mentioned in "4.4.1 Struct Literals" of "The Go Programming Language". According to this,
h := &Hello{}
is equivalent to
h := new(Hello)
*h = Hello{}
it says[1]. In other words, h := &Hello{} seems to function as a kind of syntactic sugar. By the way, if you define a method as
func (h *Hello) Say() string {
return "Hello"
}
with a pointer receiver, then
fmt.Println(Hello{}.Say()) // cannot call pointer method on Hello{}
is not allowed (since implicit conversion to a pointer type is not possible with literals), but if you wrap it in parentheses to be explicit like
fmt.Println((&Hello{}).Say()) // Hello
it will be instantiated, so it won't be a compilation error. Note that
fmt.Println(&Hello{}.Say())
// cannot take the address of (&Hello{}).Say()
// cannot call pointer method on Hello{}
will result in a compilation error (due to the scope of & extending to Hello{}.Say()).
Reading the language specification carefully, it says:
Calling the built-in function new or taking the address of a composite literal allocates storage for a variable at run time. Such an anonymous variable is referred to via a (possibly implicit) pointer indirection.
(via “The Go Programming Language Specification”)
In other words,
fmt.Printf("%p\n", &[3]int{1, 2, 3}) // print pointer to array
fmt.Printf("%p\n", &[]int{4, 5, 6}) // print pointer to slice
fmt.Printf("%p\n", &map[string]string{"foo": "bar"}) // print pointer to map
is also acceptable. I've become a bit wiser again (lol).
[Bonus] Literal Values and Methods
Even in cases where the type has a basic type as its underlying type (not just composite types), for example,
type Name string
func (n Name) Say() string {
return strings.Join([]string{"This is", string(n), "speaking!"}, " ")
}
if we have such a type and method,
fmt.Println(Name("Hayakawa").Say()) // This is Hayakawa speaking!
it works without any issues (note that Name("Hayakawa") is a type conversion, not a function call). However,
func (n *Name) Say() string {
return strings.Join([]string{"This is", string(*n), "speaking!"}, " ")
}
if we change the method receiver to a pointer type, then
fmt.Println(Name("Hayakawa").Say())
// cannot call pointer method on Name("Hayakawa")
// cannot take the address of Name("Hayakawa")
and even
fmt.Println((&Name("Hayakawa")).Say())
// cannot take the address of Name("Hayakawa")
both result in compilation errors. As mentioned in the previous section (excluding syntactic sugar like &struct{}{}), you cannot obtain a pointer value directly from a literal expression, so it cannot be implicitly converted to a pointer type when the method is called.
Of course, if you assign it to a variable,
n := Name("Hayakawa")
fmt.Println(n.Say()) // This is Hayakawa speaking!
fmt.Println((&n).Say()) // This is Hayakawa speaking!
No problem. It's confusing.
[Update 2022-03-02] Addressing Slices and Maps
Information shared in the "Go Programming Language" community on Twitter.
In the main text, I mentioned that the pointer value of a map literal can be obtained,
fmt.Printf("%p\n", &map[string]string{"foo": "bar"}) // print pointer to map
but the pointer value of an element obtained using square brackets ([ ]) cannot be obtained and results in a compilation error.
fmt.Printf("%v", map[string]int{"foo": 1, "bar": 2}["foo"]) // 1
fmt.Printf("%p", &map[string]int{"foo": 1, "bar": 2}["foo"]) // cannot take the address of map[string]int{...}["foo"]
Actually, this is a specification of the map type, regardless of whether it's a literal or not.
m := map[string]int{"foo": 1, "bar": 2}
fmt.Printf("%p", &m["foo"]) // cannot take the address of m["foo"]
Regarding this, Chapter 4.3 of the book "The Go Programming Language" explains it as follows:
One reason that you cannot take the address of a map element is that a map grow might cause existing elements to be rehashed into new storage locations, potentially invalidating the address.
(“The Go Programming Language” Chapter 4.3)
It seems to mean that you can't get the pointer value of an element because the relative positions of map elements might change randomly for some reason.
On the other hand, since the relative positions of elements in a slice are fixed,
s := []int{1, 2, 3}
fmt.Printf("%p\n", &s[0]) // print pointer to element in slice
fmt.Printf("%p\n", &[]int{1, 2, 3}[0]) // print pointer to element in slice
you can take the pointer value to an element for both normal variables and literals.
Discussion