Объясните утверждения типа в Go

Я читаю об утверждениях типаx.(T) вЯзык программирования Go и не понимаю их.

Я понимаю, что есть разные сценарии:

T - это конкретный тип или интерфейсОдно (заявленное значение?) Или два (нормально) значения могут быть возвращены

Вот чего я не понимаю:

Зачем мне их использовать?Что именно они возвращают?

Я также гуглил по теме и до сих пор не понимаю.

Ответы на вопрос(3)

проверьте, имеет ли возвращаемая ошибка тип T.

https://golang.org/ref/spec#Type_assertions

Для одного утверждения о возвращаемом значении: при неудаче программа запаникует.

Для утверждения с двумя возвращаемыми значениями: в случае неудачи второй аргумент устанавливается в значение false, и программа не паникует.

где x имеет тип интерфейса, а T является типом. Кроме того, фактическое значение, хранящееся в x, имеет тип T, и T должен удовлетворять типу интерфейса x.

Решение Вопроса

x.(T) утверждает, чтоx не ноль и что значение хранится вx имеет типT.

Зачем мне их использовать:

Проверятьx это нольпроверить, может ли он быть преобразован (утвержден) в другой типконвертировать (утверждать) в другой тип

Что именно они возвращают:

t := x.(T) => т имеет типT; еслиx это ноль, это паника.

t,ok := x.(T) => еслиx ноль или нет типаT =>ok являетсяfalse иначеok являетсяtrue а такжеt имеет типT.

Представьте, что вам нужно рассчитать площадь 4 различных форм: круг, квадрат, прямоугольник и треугольник. Вы можете определить новые типы с помощью нового методаArea(), как это:

type Circle struct {
    Radius float64
}
func (t Circle) Area() float64 {
    return math.Pi * t.Radius * t.Radius
}

И дляTriangle:

type Triangle struct {
    A, B, C float64 // lengths of the sides of a triangle.
}
func (t Triangle) Area() float64 {
    p := (t.A + t.B + t.C) / 2.0 // perimeter half
    return math.Sqrt(p * (p - t.A) * (p - t.B) * (p - t.C))
}

И дляRectangle:

type Rectangle struct {
    A, B float64
}

func (t Rectangle) Area() float64 {
    return t.A * t.B
}

И дляSquare:

type Square struct {
    A float64
}
func (t Square) Area() float64 {
    return t.A * t.A
}

Здесь у вас естьCircle, с радиусом 1,0, и другие формы с их сторонами:

shapes := []Shape{
    Circle{1.0},
    Square{1.772453},
    Rectangle{5, 10},
    Triangle{10, 4, 7},
}

Интересно! Как мы можем собрать их всех в одном месте?
Сначала вам нужноShape interface собрать их всех в один кусочек формы[]Shape :

type Shape interface {
    Area() float64
}

Теперь вы можете собрать их так:

shapes := []Shape{
    Circle{1.0},
    Square{1.772453},
    Rectangle{5, 10},
    Triangle{10, 4, 7},
}

В конце концов,Circle этоShape а такжеTriangle этоShape тоже.
Теперь вы можете распечатать область каждой фигуры с помощью одного оператораv.Area() :

for _, v := range shapes {
    fmt.Println(v, "\tArea:", v.Area())
}

ТакArea() это общий интерфейс между всеми формами. Теперь, как мы можем вычислить и вызвать необычный метод, такой как углы треугольника, используя вышеshapes?

func (t Triangle) Angles() []float64 {
    return []float64{angle(t.B, t.C, t.A), angle(t.A, t.C, t.B), angle(t.A, t.B, t.C)}
}
func angle(a, b, c float64) float64 {
    return math.Acos((a*a+b*b-c*c)/(2*a*b)) * 180.0 / math.Pi
}

Теперь пришло время извлечьTriangle сверхуshapes:

for _, v := range shapes {
    fmt.Println(v, "\tArea:", v.Area())
    if t, ok := v.(Triangle); ok {
        fmt.Println("Angles:", t.Angles())
    }
}

С помощьюt, ok := v.(Triangle) мы запросили утверждения типа, то есть мы попросили компилятор попытаться преобразоватьv типаShape печататьTriangle, так что если он успешен,ok будетtrue иначеfalse, а затем, если это успешно, вызовt.Angles() рассчитать три угла треугольника.

Это вывод:

Circle (Radius: 1)  Area: 3.141592653589793
Square (Sides: 1.772453)    Area: 3.1415896372090004
Rectangle (Sides: 5, 10)    Area: 50
Triangle (Sides: 10, 4, 7)  Area: 10.928746497197197
Angles: [128.68218745348943 18.194872338766785 33.12294020774379]

И весь рабочий пример кода:

package main

import "fmt"
import "math"

func main() {
    shapes := []Shape{
        Circle{1.0},
        Square{1.772453},
        Rectangle{5, 10},
        Triangle{10, 4, 7},
    }
    for _, v := range shapes {
        fmt.Println(v, "\tArea:", v.Area())
        if t, ok := v.(Triangle); ok {
            fmt.Println("Angles:", t.Angles())
        }
    }
}

type Shape interface {
    Area() float64
}
type Circle struct {
    Radius float64
}
type Triangle struct {
    A, B, C float64 // lengths of the sides of a triangle.
}
type Rectangle struct {
    A, B float64
}
type Square struct {
    A float64
}

func (t Circle) Area() float64 {
    return math.Pi * t.Radius * t.Radius
}

// Heron's Formula for the area of a triangle
func (t Triangle) Area() float64 {
    p := (t.A + t.B + t.C) / 2.0 // perimeter half
    return math.Sqrt(p * (p - t.A) * (p - t.B) * (p - t.C))
}
func (t Rectangle) Area() float64 {
    return t.A * t.B
}

func (t Square) Area() float64 {
    return t.A * t.A
}

func (t Circle) String() string {
    return fmt.Sprint("Circle (Radius: ", t.Radius, ")")
}
func (t Triangle) String() string {
    return fmt.Sprint("Triangle (Sides: ", t.A, ", ", t.B, ", ", t.C, ")")
}
func (t Rectangle) String() string {
    return fmt.Sprint("Rectangle (Sides: ", t.A, ", ", t.B, ")")
}
func (t Square) String() string {
    return fmt.Sprint("Square (Sides: ", t.A, ")")
}

func (t Triangle) Angles() []float64 {
    return []float64{angle(t.B, t.C, t.A), angle(t.A, t.C, t.B), angle(t.A, t.B, t.C)}
}
func angle(a, b, c float64) float64 {
    return math.Acos((a*a+b*b-c*c)/(2*a*b)) * 180.0 / math.Pi
}

Также см:

Введите утверждения

Для выражения x типа интерфейса и типа T основное выражение

x.(T)  

утверждает, что x не равен nil и что значение, хранящееся в x, имеет тип T. Обозначение x. (T) называется утверждением типа.

Точнее, если T не является интерфейсным типом, x. (T) утверждает, что динамический тип x идентичен типу T. В этом случае T должен реализовать тип (interface) x; в противном случае утверждение типа недопустимо, поскольку для x невозможно сохранить значение типа T. Если T является типом интерфейса, x. (T) утверждает, что динамический тип x реализует интерфейс T.

Если утверждение типа выполнено, значением выражения является значение, хранящееся в x, а его тип - T.Если утверждение типа ложно, возникает паника во время выполнения. Другими словами, даже если динамический тип x известен только во время выполнения, тип x. (T) известен как T в правильной программе.

var x interface{} = 7  // x has dynamic type int and value 7
i := x.(int)           // i has type int and value 7

type I interface { m() }
var y I
s := y.(string)        // illegal: string does not implement I (missing method m)
r := y.(io.Reader)     // r has type io.Reader and y must implement both I and io.Reader

Утверждение типа, используемое в присваивании или инициализации специальной формы

v, ok = x.(T)
v, ok := x.(T)
var v, ok = x.(T)

дает дополнительное нетипизированное логическое значение. Значение ok равно true, если утверждение верно. В противном случае оно равно false, а значение v является нулевым значением для типа T.В этом случае не возникает паника во время выполнения.

Редактировать:
Вопрос: что означает утверждениеx.(T) вернуться, когда Т являетсяinterface{} а не конкретный тип?
Ответ:

Он утверждает, что x не равен nil и что значение, хранящееся в x, имеет тип T.

например эта паника (компиляция: успех, запуск:panic: interface conversion: interface is nil, not interface {}):

package main

func main() {
    var i interface{} // nil
    var _ = i.(interface{})
}

И это работает (Run: OK):

package main

import "fmt"

func main() {
    var i interface{} // nil
    b, ok := i.(interface{})
    fmt.Println(b, ok) // <nil> false

    i = 2
    c, ok := i.(interface{})
    fmt.Println(c, ok) // 2 true

    //var j int = c // cannot use c (type interface {}) as type int in assignment: need type assertion
    //fmt.Println(j)
}

выход:

<nil> false
2 true

Обратите внимание:c имеет типinterface {} и неint.

Посмотрите этот рабочий пример кода с закомментированными выводами:

package main

import "fmt"

func main() {
    const fm = "'%T'\t'%#[1]v'\t'%[1]v'\t%v\n"
    var i interface{}
    b, ok := i.(interface{})
    fmt.Printf(fm, b, ok) // '<nil>'    '<nil>' '<nil>' false

    i = 2
    b, ok = i.(interface{})
    fmt.Printf(fm, b, ok) // 'int'  '2' '2' true

    i = "Hi"
    b, ok = i.(interface{})
    fmt.Printf(fm, b, ok) // 'string'   '"Hi"'  'Hi'    true

    i = new(interface{})
    b, ok = i.(interface{})
    fmt.Printf(fm, b, ok) // '*interface {}'    '(*interface {})(0xc042004330)' '0xc042004330'  true

    i = struct{}{}
    b, ok = i.(interface{})
    fmt.Printf(fm, b, ok) // 'struct {}'    'struct {}{}'   '{}'    true

    i = fmt.Println
    b, ok = i.(interface{})
    fmt.Printf(fm, b, ok) // 'func(...interface {}) (int, error)'   '(func(...interface {}) (int, error))(0x456740)'    '0x456740'  true

    i = Shape.Area
    b, ok = i.(interface{})
    fmt.Printf(fm, b, ok) // 'func(main.Shape) float64' '(func(main.Shape) float64)(0x401910)'  '0x401910'  true
}

type Shape interface {
    Area() float64
}
 user128377608 авг. 2016 г., 08:43
Отличное объяснение! Теперь я понимаю это намного лучше. Еще одна вещь, если я могу попросить вас дополнить: что возвращает утверждение, когда T является интерфейсом, а не конкретным типом?
 user616939908 авг. 2016 г., 09:59
@ user1283776:var _ = i.(interface{}) еслиi Ноль это паникует. но вb, ok := i.(interface{}) еслиi тогда нольok ложно, иначеok это правда иb имеет типinterface{}и посмотрите конец этого поста для примеров кодов.

Ваш ответ на вопрос