Skip to content

proposal: spec: extended type inference for make and new #34515

Open
@DeedleFake

Description

@DeedleFake

Rationale

Currently in Go, type inference works in one direction and one direction only: From the values of an assignment to the variable declarations. In other words, given some expression with a statically-determinable type, which is basically every expression, the type of a declaration can be omitted. This allows for a lot of the perceived lightness of Go over many statically typed languages.

However, there are a number of situations where this is not possible, most of which have to do with make() and new(), both of which are unique, not including some stuff in the unsafe package, in that they take a type name as an argument. Normally this is a non-issue, as that type can be used to determine the return of the expression, thus allowing for inference in the normal manner:

m := make(map[string]interface{})
s := make([]string, 0, 10)
p := new(int)

Sometimes the variable must have a separately declared type, though, such as in the case of struct fields:

type Example struct {
  m map[string]interface{}
}

func NewExample() *Example {
  return &Example{
    m: make(map[string]interface{}),
  }
}

This leads to unwanted repetition of the type name, making later alteration more awkward. In particular, I thought of this while reading through one of the many proposals about ways to make anonymous structs more useful with channels, and I realized that the following pattern could get very annoying, exacerbating the existing issue:

type Example struct {
  c chan struct {
    v string
    r chan<- int
  }
}

func NewExample() *Example {
  return &Example{
    c: make(chan struct {
      v string
      r chan<- int
    },
  }
}

Proposal

I propose the introduction of a new keyword, say typeof, which takes a variable, function name, or a struct field identifier and essentially acts as a stand-in for a standard type name, possibly with the restriction of only being available as the argument to a make() or new() call. For example,

return &Example{
  m: make(typeof Example.m),
}

This would allow a type name to be pegged to an already declared variable type elsewhere.

Alternative

Alternatively, make() and new() could allow for the aforementioned types of identifiers directly, such as

return &Example{
  m: make(Example.m),
}

This has the advantage of being backwards compatible, but is potentially less flexible if one wants to extend the functionality elsewhere later, such as to generics.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions