GO Language Interview Essence - What is the difference between type conversion and assertion?

We know that implicit type conversion is not allowed in the Go language, that is to say, =variables with different types are not allowed to appear on both sides.

类型转换, 类型断言the essence is to convert one type into another type. The difference is that type assertions operate on interface variables.

type conversion

For 类型转换, the two types before and after conversion must be compatible with each other. The syntax for type conversion is:

<result type> := <target type> ( <expression> )

package main

import "fmt"

func main() {
	var i int = 9

	var f float64
	f = float64(i)
	fmt.Printf("%T, %v\n", f, f)

	f = 10.8
	a := int(f)
	fmt.Printf("%T, %v\n", a, a)

	// s := []int(i)
}

In the above code, I defined a variable of inttype and float64type and tried to convert each other before them. The result was successful: inttype and float64are compatible with each other.

If I uncomment the last line of code, the compiler will report a type incompatibility error:

cannot convert i (type int) to type []int

affirmation

As mentioned before, because the empty interface interface{}does not define any functions, all types in Go implement the empty interface. When the formal parameter of a function is interface{}, then in the function, an assertion needs to be made on the formal parameter to obtain its true type.

The syntax for assertions is:

<value of target type>, <Boolean parameter> := <expression>.(target type) // Safe type assertion <
value of target type> := <expression>.(target type) //Unsafe type assertion

Type conversion is somewhat similar to type assertion. The difference is that type assertion operates on the interface.

Let’s look at a brief example:

package main

import "fmt"

type Student struct {
	Name string
	Age int
}

func main() {
	var i interface{} = new(Student)
	s := i.(Student)
	
	fmt.Println(s)
}

Run it:

panic: interface conversion: interface {
    
    } is *main.Student, not main.Student

Directly panic, this is because iit is *Studentof type, not Studentof type, and the assertion fails. This happened directly panic. Online code may not be suitable for this. You can use the "safety assertion" syntax:

func main() {
	var i interface{} = new(Student)
	s, ok := i.(Student)
	if ok {
		fmt.Println(s)
	}
}

This way, even if the assertion fails, it won't panic.

There is actually another form of assertion, which is used to switchdetermine the type of the interface using statements. Each casewill be considered sequentially. When a is hit , the statements in casewill be executed , so the order of statements is very important, because there may be multiple matches.casecasecase

The code example is as follows:

func main() {
	//var i interface{} = new(Student)
	//var i interface{} = (*Student)(nil)
	var i interface{}

	fmt.Printf("%p %v\n", &i, i)

	judge(i)
}

func judge(v interface{}) {
	fmt.Printf("%p %v\n", &v, v)

	switch v := v.(type) {
	case nil:
		fmt.Printf("%p %v\n", &v, v)
		fmt.Printf("nil type[%T] %v\n", v, v)

	case Student:
		fmt.Printf("%p %v\n", &v, v)
		fmt.Printf("Student type[%T] %v\n", v, v)

	case *Student:
		fmt.Printf("%p %v\n", &v, v)
		fmt.Printf("*Student type[%T] %v\n", v, v)

	default:
		fmt.Printf("%p %v\n", &v, v)
		fmt.Printf("unknow\n")
	}
}

type Student struct {
	Name string
	Age int
}

mainThere are three different lines of declarations in the function. Run one line each time and comment the other two lines to get three sets of running results:

// --- var i interface{
    
    } = new(Student)
0xc4200701b0 [Name: ], [Age: 0]
0xc4200701d0 [Name: ], [Age: 0]
0xc420080020 [Name: ], [Age: 0]
*Student type[*main.Student] [Name: ], [Age: 0]

// --- var i interface{
    
    } = (*Student)(nil)
0xc42000e1d0 <nil>
0xc42000e1f0 <nil>
0xc42000c030 <nil>
*Student type[*main.Student] <nil>

// --- var i interface{
    
    }
0xc42000e1d0 <nil>
0xc42000e1e0 <nil>
0xc42000e1f0 <nil>
nil type[<nil>] <nil>

For the first line of statement:

var i interface{} = new(Student)

iIt is a *Studenttype, matching the third case. Judging from the three printed addresses, the variables in these three places are actually different. mainThere is a local variable in the function ; iwhen calling the function, a copy of the parameters is actually copied, so there is another variable in the function v, which is ia copy of; after the assertion, a new copy is generated. So the addresses of the three variables finally printed are different.

For the second line of statements:

var i interface{} = (*Student)(nil)

What I want to explain here is that ithe dynamic type here is (*Student), the data is nil, its type is not nil, and when it nilis compared with, the result obtained is also false.

The last line of statement:

var i interface{}

This time iis nilthe type.

[Extension 1]
fmt.PrintlnThe parameters of the function are interface. For built-in types, the function will use an exhaustive method to find out its true type, and then convert it to a string for printing. For a custom type, first determine whether the type implements String()the method. If it does, the result of the method will be printed directly String(); otherwise, the members of the object will be traversed through reflection for printing.

Let’s look at a short example again, it’s relatively simple, don’t be nervous:

package main

import "fmt"

type Student struct {
	Name string
	Age int
}

func main() {
	var s = Student{
		Name: "qcrao",
		Age: 18,
	}

	fmt.Println(s)
}

Because Studentthe structure does not implement String()the method, fmt.Printlnreflection will be used to print the member variables one by one:

{
    
    qcrao 18}

Add an String()implementation of the method:

func (s Student) String() string {
	return fmt.Sprintf("[Name: %s], [Age: %d]", s.Name, s.Age)
}

Print the result:

[Name: qcrao], [Age: 18]

Print according to our customized method.

[Extension 2]
Regarding the above example, if you change it:

func (s *Student) String() string {
	return fmt.Sprintf("[Name: %s], [Age: %d]", s.Name, s.Age)
}

Note that the receiver types of the two functions are different. Now Studentthe structure only has one function with the receiver type. Print the result 指针类型:String()

{
    
    qcrao 18}

Why?

A type has only methods whose Treceivers are and ; a type has methods whose receivers are and . Syntactically, the methods that can be directly adjusted are just syntactic sugar for .T*TT*TT*TGo

Therefore, when Studentthe structure defines String()a method whose receiver type is a value type, pass

fmt.Println(s)
fmt.Println(&s)

All can be printed in customized formats.

If Studentthe structure defines String()a method whose receiver type is a pointer type, it can only be passed

fmt.Println(&s)

to print in a custom format.

Guess you like

Origin blog.csdn.net/zy_dreamer/article/details/132795760