SoFunction
Updated on 2025-03-03

Go Language Selector Example Tutorial

introduction

In Go language, expressionsIt may mean two things. If foo is a package name, then the expression is a so-called qualified identifier that refers to the exported identifier in the package foo. Since it is only used to process exported identifiers, bar must start with capital letters (translator's note: if the initial letter is capitalized, it can be accessed by other packages; if the initial letter is lowercase, it can only be used in this package):

package foo
import "fmt"
func Foo() {
    ("foo")
}
func bar() {
    ("bar")
}
package main
import "/mlowicki/foo"
func main() {
    ()
}

This program will work properly. But (main function) call()An error will be reported during compilation — cannot refer to unexported name (unexported name cannot be referenced).

If foo is not a package name, thenIt is a selector expression. It accesses fields or methods of foo expressions. The identifier after the point is called a selector (selector). The rules regarding capitalization of the initial letter do not apply here. It allows the selection of unexported fields or methods from packages that define the type foo:

package main
import "fmt"
type T struct {
    age byte
}
func main() {
    (T{age: 30}.age)
}

The program prints:

30

The depth of the selector

The language specification defines the depth (depth) of the selector. Let's see how it works. Selector expressionIt can represent fields or methods defined in type foo or an anonymous field defined in type foo:

type E struct {
    name string
}
func (e E) SayHi() {
    ("Hi %s!\n", )
}
type T struct {
    age byte
    E
}
func (t T) IsStillYoung() bool {
    return  <= 18
}
func main() {
    t := T{30, E{"Michał"}}
    (()) // false
    () // 30
    () // Hi Michał!
    () // Michał
}

In the above code, we can see that the method can be called or accessed fields defined in embedded fields. Fieldand methodsAll have been promoted, because type E is nested in the definition of T:

type T struct {
    age byte
    E
}

The depth of the selector that represents a field or type in type T is 0 (translator's note: the depth of the selector that represents a field or method defined in type T is 0). If a field or method is defined in an embedded (that is, anonymous) field, then the depth is equal to the number of such fields or methods traversing the anonymous field. In the previous clip, the depth of the age field is 0 because it is declared in T, but because E is placed in T, the depth of name or SayHi is 1. Let's take a look at more complex examples:

package main
import "fmt"
type A struct {
    a string
}
type B struct {
    b string
    A
}
type C struct {
    c string
    B
}
func main() {
    v := C{"c", B{"b", A{"a"}}}
    () // c
    () // b
    () // a
}
  • The depth of c is, its value is 0. This is because the fields are declared in C
  • The depth of mid b is 1. This is because its fields are defined in type B, which (type B) is embedded in C
  • The depth of the medium a is 2. This is because it is necessary to traverse two anonymous fields (B and A) to access it

Valid selector

There are clear rules in the go language about which selectors are valid and which are invalid. Let's get to know them in depth.

Uniqueness + shallowest depth

When T is not a pointer or interface type, the first rule applies to the typeTand*T. The selector represents the shallowest depth of the field and method in the type T that defines bar. At such a depth, it is possible to define a (unique) such field or method source code:

type A struct {
    B
    C
}
type B struct {
    age byte
    name string
}
type C struct {
    age byte
    D
}
type D struct {
    name string
}
func main() {
    a := A{B{1, "b"}, C{2, D{"d"}}}
    (a) // {{1 b} {2 {d}}}
    // () ambiguous selector 
    () // b
}

The structure of type embedding is as follows:

 A
 / \
B   C
     \
      D

The selector is valid and indicates that the depth of the field name (within type B) is 1. The field name in type C is "shadowed". The related age fields are different. There are two fields like this at depth 1 (in the B and C types), so the compiler will throwambiguous selector mistake.

Gopher can still use the full selector when the promoted field or method is ambiguous.

()   // b
() // d
()   // d

It is worth reiterating that the rule also applies to*T-- example.

Empty pointer

package main
import "fmt"
type T struct {
    num int
}
func (t T) m() {}
func main() {
    var p *T
    ()
    ()
}

If the selector is valid, but foo is a null pointer, then the evaluation causes

runtime panic:panic invalid memory address or nil pointer dereference

interface

If foo is an interface type value, then it is actually a method of the dynamic value of foo:

type I interface {
    m()
}
type T struct{}
func (T) m() {
    ("I'm alive!")
}
func main() {
    var i I
    i = T{}
    ()
}

The above clip outputI'm alive!. Of course, when calling a method that is not in the interface's method collection, a compile-time error will occur, such as

  undefined (type I has no field or method f)

If foo is nil, then it will cause a runtime error:

type I interface {
    f()
}
func main() {
    var i I
    ()
}

Such a program will be due to errorspanic: runtime error: invalid memory address or nil pointer dereferenceAnd collapsed.

This is similar to the case of null pointers, and errors occur due to such as no value assignment and interface zero value is nil.

A special situation

In addition to the description of valid selectors so far, this also has a scenario: suppose there is a named pointer type here:

type P *T

The method set of type P does not contain any method of type T. If there is a variable of type P, no T method can be called. However, the specification allows the selection of fields (non-method) of type T:

type T struct {
    num int
}
func (t T) m() {}
type P *T
func main() {
    var p P = &T{num: 10}
    ()
    // () // compile-time error:  undefined (type P has no field or method m)
    (*p).m()
}

Converted to(*p).num

Under hood

If you are interested in the actual implementation of selector facing and verification, please check it outselectorandLookupFieldOrMethodFunction.

The above is the detailed content of the Go language selector instance tutorial. For more information about the Go selector tutorial, please follow my other related articles!