New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new(T) and &T{} result into different things : Document clarification needed #29425

Open
pooriaPoorsarvi opened this Issue Dec 26, 2018 · 12 comments

Comments

Projects
None yet
5 participants
@pooriaPoorsarvi
Copy link

pooriaPoorsarvi commented Dec 26, 2018

What version of Go are you using (go version)?

$ go version 1.11.1.

Does this issue reproduce with the latest release?

yes

What did you do?

based on what is said on https://golang.org/doc/effective_go.html#composite_literals and https://stackoverflow.com/questions/27827871/what-the-difference-between-tnil-and-t-newt-golang
there should be no difference between new(T) and &T{} but as you can see in https://play.golang.org/p/R3yxz772TvT

package main

import (
	"fmt"
)

func main() {
	arr1 := new([]int)
	fmt.Println(*arr1, len(*arr1))
	fmt.Println(*arr1 == nil)
	fmt.Println(arr1 == nil)
	fmt.Println()
	arr2 :=  &[]int {}
	fmt.Println(*arr2, len(*arr2))
	fmt.Println(*arr2 == nil)
	fmt.Println(arr2 == nil)
	fmt.Println()
}
/*
output : 
[] 0
true
false

[] 0
false
false

*/

the result says otherwise and these expression result into different things because one is pointing to the nil value of the type and the other one isn't.
any kind of clarification would be much appreciated.

@go101

This comment has been minimized.

Copy link

go101 commented Dec 26, 2018

This is normal. Please read the last section in this article for details.
arr1 := *new([]int) <=> var arr1 []int <=> var arr1 = []int(nil)

@pooriaPoorsarvi

This comment has been minimized.

Copy link

pooriaPoorsarvi commented Dec 26, 2018

This is normal. Please read the last section in this article for details.
arr1 := new([]int) <=> var arr1 []int <=> var arr1 = []int(nil)

well, you're not talking about composite literals as is discussed in the links that I have provided. I just want to know why do we consider new([]int) and &[]int{} equivalent while their comparison to nil result into different boolean values?

@go101

This comment has been minimized.

Copy link

go101 commented Dec 26, 2018

new(T) will allocated a memory block for a T value and reset the T value as the zero value of type T. This is true for every type, not only for slice types.

And []int{} is not a zero slice value. It is a slice value without any elements.

@pooriaPoorsarvi

This comment has been minimized.

Copy link

pooriaPoorsarvi commented Dec 26, 2018

new(T) will allocated a memory block for a T value and reset the T value as the zero value of type T. This is true for every type, not only for slice types.

And []int{} is not a zero slice value. It is a slice value without any elements.

So based on what you're saying new(T) and &T{} are not equivalent for every T, am I right?

@go101

This comment has been minimized.

Copy link

go101 commented Dec 26, 2018

Generally, you can think so.
&T{} is only valid for 4 kinds of types: slice, map, array and struct.
For slice and map types, new(T) != &T{} for sure.
For an array or struct type, if its size is not zero, new(T) != &T{} is always true.
For for an array or struct type with zero size, the result of new(T) != &T{} can be either true or false, which is compiler dependent.

@go101

This comment has been minimized.

Copy link

go101 commented Dec 26, 2018

An example:

package main

import (
	"fmt"
)

var print = fmt.Println

func main() {
	p1 := &[0]int{}
	p2 := new([0]int)
	print(p1, p2)    // try to comment off this line to see the effect.
	print (p1 == p2) // true or false
}

See this for details.

@pooriaPoorsarvi

This comment has been minimized.

Copy link

pooriaPoorsarvi commented Dec 26, 2018

An example:

package main

import (
	"fmt"
)

var print = fmt.Println

func main() {
	p1 := &[0]int{}
	p2 := new([0]int)
	print(p1, p2)    // try to comment off this line to see the effect.
	print (p1 == p2) // true or false
}

See this for details.

well, this example is irrelevant to what I am asking because your code may result in printing the value of false because the address of the objects may be different, but I'm concerned about the value of the objects and not their address. That's why I have considered using *arr1 and *arr2 to compare to nil and in my case, the answer is always true for *arr1 and always false for *arr2. I have compared them to nil because slices can only be compared to nil. just to make things more clarified, this is the part of the code that I am concerned about :

package main

import (
	"fmt"
)

func main() {
	arr1 := new([]int)
	fmt.Println(*arr1 == nil)
	fmt.Println()
	arr2 :=  &[]int {}
	fmt.Println(*arr2 == nil)
	fmt.Println()
}
/*
output : 
true

false

*/

because if both new and composite literals do the same thing, in the case that we don't give any input for our composite literal, the value of our objects should be the same and if they don't result in the same value, I think it should be clarified in the documentation because answers in stackoverflow.com and golang.org clearly point towards the value of the objects being the same.

@go101

This comment has been minimized.

Copy link

go101 commented Dec 27, 2018

Go specs says the followings:

The value of an uninitialized slice is nil.

A slice, once initialized, is always associated with an underlying array that holds its elements.

A slice literal describes the entire underlying array literal. Thus the length and capacity of a slice literal are the maximum element index plus one. A slice literal has the form
[]T{x1, x2, … xn}
and is shorthand for a slice operation applied to an array:
tmp := [n]T{x1, x2, … xn}
tmp[0 : n]

So a []T{} is equivalent to tmp := [0]T{}; tmp[0:0] , it is initialized, not uninitialized.

However, it looks, the spec doesn't associate uninitialized values to zero values. In particular, there is a line makes people feel that uninitialized values are not zero values.

... all the result values are initialized to the zero values for their type ...

Also, the spec doesn't explicitly mention the zero values of slice type are nil, it just say uninitialized slices are nil.

Maybe I haven't read the spec carefully. @griesemer

@griesemer griesemer self-assigned this Dec 27, 2018

@griesemer griesemer added this to the Go1.12 milestone Dec 27, 2018

@griesemer

This comment has been minimized.

Copy link
Contributor

griesemer commented Dec 27, 2018

p1 := new([]int) allocates a new variable of type []int on the heap; its (the variable's) value (*p1) is the zero value which is nil for slices. In contrast, p2 := &[]int{} makes a composite literal of type []int with 0 elements and returns a pointer to it. The composite literal's value (*p2) is a slice with 0 elements, which is not the same as a nil slice (even though a nil slice also has zero elements). The behavior you are describing is correct.

I think the documentation is fairly clear on this but I will review it to see if it can be improved.

@pooriaPoorsarvi

This comment has been minimized.

Copy link

pooriaPoorsarvi commented Dec 27, 2018

p1 := new([]int) allocates a new variable of type []int on the heap; its (the variable's) value (*p1) is the zero value which is nil for slices. In contrast, p2 := &[]int{} makes a composite literal of type []int with 0 elements and returns a pointer to it. The composite literal's value (*p2) is a slice with 0 elements, which is not the same as a nil slice (even though a nil slice also has zero elements). The behavior you are describing is correct.

I think the documentation is fairly clear on this but I will review it to see if it can be improved.

Thanks a lot for your answer :D .

@bradfitz

This comment has been minimized.

Copy link
Member

bradfitz commented Dec 28, 2018

p1 := new([]int) allocates a new variable of type []int on the heap

Well, not "on the heap". It just allocates it somewhere. The stack-vs-heap distinction doesn't exist in the spec. (You know this, but clarifying for others).

@griesemer

This comment has been minimized.

Copy link
Contributor

griesemer commented Dec 29, 2018

Yes, indeed. Thanks for clarifying, @bradfitz!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment