2015-08-08 5 views
4

мне было интересно, почему вы не можете сделать:Почему вы не можете преобразовать типы Slice?

type Foo struct { A int } 
type Bar Foo 

foos := []Foo{Foo{1}, Foo{2}} 
bars := []Bar(foos) 
//cannot convert foos (type []Foo) to type []Bar 

, и я узнал, что это потребует выполнения, чтобы выполнить цикл по кусочку для преобразования каждого из элементов, которые были бы не-идиоматическими Go , Это имеет смысл.

Однако, возможно, это не будет решаться компилятором только сглаживание Bar как Foo, так что внутри они одинаковы, и они используют заголовок того же типа внизу? Я предполагаю, что ответ - нет, хотя мне любопытно, почему.

+0

Вы, по сути, спрашиваете, как работать с системой типов, чтобы оптимизировать цикл. Я думаю, что пакет ['unsafe'] (http://golang.org/pkg/unsafe/) позволит вам сделать это – Kos

ответ

3

Это:

[]Bar(foos) 

тип conversion. Конверсии имеют конкретные правила в соответствии со спецификацией:

Непостоянного значение x может быть преобразован в типе T в любом из этих случаев:

  • x является assignable к T.
  • x тип и T имеют одинаковые базовые типы.
  • x тип и T являются неназванными типами указателей, а их базовые типы указателей имеют одинаковые базовые типы.
  • x тип и T являются целыми или с плавающей точкой.
  • x тип и T являются сложными типами.
  • x - целое число или кусочек байтов или рун, а T - это строковый тип.
  • x - это строка, а T - фрагмент байтов или рун.

Здесь не применяется. Зачем?

Поскольку базовый тип []Foo не совпадает с базовым типом []Bar. И значение типа []Foo не может быть присвоено переменной типа []Bar, см. Assignability rules here.

Основной тип Foo такой же, как основной тип Bar, но то же самое не относится к ломтиков, когда тип элемента является Foo и Bar.

Так следующие работы:

type Foo struct{ A int } 

type Foos []Foo 
type Bars Foos 

func main() { 
    foos := []Foo{Foo{1}, Foo{2}} 
    bars := Bars(foos) 

    fmt.Println(bars) 
} 

Output (попробуйте на Go Playground):

[{1} {2}] 

Обратите внимание, что с момента фактического представления памяти Foo и Bar одно и то же (так как основной тип Bar - Foo), в этом случае с использованием пакета unsafe вы можете «просмотреть» значение []Foo в качестве значения []Bar:

type Foo struct{ A int } 
type Bar Foo 

func main() { 
    foos := []Foo{Foo{1}, Foo{2}} 

    bars := *(*[]Bar)(unsafe.Pointer(&foos)) 

    fmt.Println(bars) 
    fmt.Printf("%T", bars) 
} 

Это: *(*[]Bar)(unsafe.Pointer(&foos)) означает, что принимают адрес foos, преобразующие его unsafe.Pointer (according to spec все указатели могут быть преобразованы в unsafe.Pointer), то это Pointer преобразуется в *[]Bar (опять же в соответствии со спецификацией Pointer может преобразуется в любой другой тип указателя), а затем этот указатель разыменовывается (оператор *), поэтому результатом является значение типа []Bar, как видно на выходе.

Output (попробуйте на Go Playground):

[{1} {2}] 
[]main.Bar 

Примечания:

Цитирование пакет док из unsafe:

Пакет небезопасным содержит операции, что шаг по типу безопасность программ Go.

Пакеты, которые импортируют небезопасные, могут быть не переносимыми и не защищены Руководством по совместимости Go 1.

Что это значит? Это означает, что вы не должны возвращаться к использованию пакета usafe каждый раз, когда это облегчает вашу жизнь. Вы должны использовать его только в исключительных случаях, когда его не использовать, это сделает вашу программу очень медленной и сложной.

В вашей программе это не так, поскольку я предложил рабочий пример с небольшим рефакторингом (Foos и Bars, являющимся срезами).

unsafe шаги по типу безопасности Go. Что это значит?Если вы измените тип foos (например, как foos := "trap!"), ваша программа все равно будет компилироваться и запускаться, но скорее всего произойдет паника. Используя usafe, вы теряете проверки типов компилятора.

Хотя, если вы используете мое другое предложение (Foos и Bars), такие изменения/опечатки обнаруживаются во время компиляции.

+0

Спасибо @icza, поэтому базовое представление такое же, это просто спецификация, которая предотвращает преобразование - Небезопасное обращение было тем, чем я был! – jpillora

+0

@jpillora См. Отредактированный ответ. Не используйте 'unsafe' только потому, что это проще. Пожалуйста, используйте правильный дизайн, чтобы избежать использования 'unsafe', если это возможно. – icza

+0

достаточно честный и согласованный - я в настоящее время перебираю каждый элемент, и я буду использовать только небезопасное, если производительность пострадает, чего, скорее всего, не будет. Может быть, этого вполне можно избежать? Вариант использования: я импортирую пакет 'foo', и я хотел бы показать его тип' foo.Foo' в моем пакете, поэтому я перепечатал его, чтобы не допустить, чтобы мои пользователи импортировали как мои пакет * и * 'foo'. – jpillora

1

Как уже упоминалось в "Why can I type alias functions and use them without casting?"

В Go, нет такого понятия, как тип псевдонима.
Ключевое слово type вводит новые named types. Они не являются псевдонимами

Если сравнивать два названных типа, имена должны соответствовать для того, чтобы им быть взаимозаменяемыми

Это то, что spec mentions:

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

Смежные вопросы