Является ли утверждение типа единственным способом возврата к указателю структуры при работе с интерфейсами? - PullRequest
0 голосов
/ 04 июня 2018

Рассмотрим следующий код:

package main

import "fmt"
// 5
type I interface {
    Foo() string
}

type a struct {
    i int
}

func (s a) Foo() string {
    return "We are here!"
}

func (s a) Bar() string {
    return "Major Tom!"
}
// 20
func main() {
    var x I = &a{i: 42}
    fmt.Println(x.Foo())
    fmt.Println(x.(*a).Bar())
}

Последнее утверждение main возвращает мне базовую структуру, но мне нужно экспортировать эту структуру, чтобы вернуться к ней.

Если я использую пакет в библиотеке, где единственным экспортируемым символом является интерфейс (большой I, маленький a в нашем примере выше между строками 5-20), то я не могу привести интерфейс коригинальный тип, когда я нахожусь в другой упаковке или файле.

Поскольку исходная структура хранится в интерфейсе, существует простой способ вернуть ее ссылку и использовать методы, которые не были объявлены в интерфейсе и только прикреплены к структуре.

Ответы [ 2 ]

0 голосов
/ 04 июня 2018

Да, в общем случае вам нужно утверждение типа (или переключатель типа ), чтобы вернуть перенесенное значение в значение интерфейса.

Но выне нужно возвращать сохраненное конкретное значение из интерфейса, чтобы иметь возможность вызывать другие методы, которые у него есть (и которые не являются частью типа интерфейса).

Вы можете ввести другое значение интерфейса иззначение интерфейса, другое значение интерфейса, тип которого содержит методы, которые вы хотите вызвать.

См. этот пример:

type Foo interface {
    Bar() string
}

type fooImpl struct{}

func (fooImpl) Bar() string { return "Bar from fooImpl" }

func (fooImpl) Baz() string { return "Baz from fooImpl" }

func main() {
    var f Foo = &fooImpl{}

    if x, ok := f.(interface{ Baz() string }); ok {
        fmt.Println(x.Baz())
    } else {
        fmt.Println("f does not have a Baz() method!")
    }
}

Foo имеет только метод Bar().У нас есть переменная f типа Foo, конкретный тип, который она хранит, - *fooImpl, у которой также есть другой метод: fooImpl.Baz().

Таким образом, мы можем ввести значение типа interface{ Baz() string } и просто позвоните Baz(), чтобы получить результат.

Вывод вышеизложенного (попробуйте на Go Playground ):

Baz from fooImpl

Утверждение типа интерфейса из другого значения интерфейса не требует экспорта типа переносимого значения.

Вы также можете создать новый тип для типа интерфейса, который вы вводите, анонимный тип не требуется:

type MyFoo interface{ Baz() string }

if x, ok := f.(MyFoo); ok {
    fmt.Println(x.Baz())
} else {
    fmt.Println("f does not have a Baz() method!")
}

Вывод такой же (попробуйте на Go Playground ).

Черт, вы даже можете "расширить" Foo с помощью дополнительных методов,и введите asset «расширенный» интерфейс:

type MyFoo interface {
    Foo
    Baz() string
}

if x, ok := f.(MyFoo); ok {
    fmt.Println(x.Bar())
    fmt.Println(x.Baz())
} else {
    fmt.Println("f does not have a Baz() method!")
}

Теперь в этом примере x является одновременно Foo и значением, которое имеет метод Baz().Вывод (попробуйте на Go Playground ):

Bar from fooImpl
Baz from fooImpl
0 голосов
/ 04 июня 2018

Да.Введите Assertion единственный способ вернуться к указателю структуры при работе с интерфейсами.

...