Инициализировать элементы структуры соответствующими элементами массива, когда количество элементов структуры равно длине массива

#arrays #go #struct #slice

#массивы #Вперед #структура #срез

Вопрос:

У меня есть такая структура и определение массива:

 type MyStruct struct {
        A int,
        B int,
        C int,
        D int,
        E int,
}

arr := [5]int{1, 2, 3, 4, 5}
 

Есть ли способ или встроенный метод, с помощью которого я могу эффективно создать новый MyStruct объект, при этом каждый элемент инициализируется arr элементами по порядку? (например, A = 1, B = 2, C = 3, D = 4, E = 5)

Комментарии:

1. В идеале вы должны использовать массивы с массивами. В противном случае вы столкнетесь с системой типов. Поэтому, если вы можете, попробуйте использовать массив в MyStruct вместо отдельных свойств.

Ответ №1:

Нет, нет. Вы должны перечислить значения в составном литерале:

 m := MyStruct{A: arr[0], B: arr[1], C: arr[2], D: arr[3], E: arr[4]}
 

Или если вы используете неключеный литерал:

 m := MyStruct{arr[0], arr[1], arr[2], arr[3], arr[4]}
 

Попробуйте это на игровой площадке Go.

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

Ответ №2:

Прежде всего, я должен подчеркнуть, что реальный ответ производственного кода для этого решения — @izca, о котором я сообщу здесь, прежде чем вы пойдете дальше по кроличьей норе:

 m := MyStruct{A: arr[0], B: arr[1], C: arr[2], D: arr[3], E: arr[4]}
 

Но должны быть другие способы сделать это. Если вы программист на C, вы знаете, что если у вас есть структура и массив одинакового размера, их можно привести, верно?


Итак, вот оно, приведение типов в стиле C (преобразование в Go) с использованием unsafe пакета:

 func main() {
    arr := [5]int{1, 2, 3, 4, 5}
    q := (*MyStruct)(unsafe.Pointer(amp;arr))
    fmt.Printf("%#vn", q)
}

type MyStruct struct {
    A int
    B int
    C int
    D int
    E int
}
 

Игровая площадка

Должны ли вы на самом деле использовать это? Нет. Я должен подчеркнуть, что это то, с чем можно поиграть, и вы не должны делать это без очень веской причины. Пакет unsafe , как можно довольно ясно вывести из его имени, небезопасен. Это не дает никаких гарантий и может нарушить что угодно, от сборщика мусора до cgo и кросс-компиляции, если не использовать их надлежащим образом. Тем не менее, считается само собой разумеющимся, что представление структур в памяти остается согласованным (что, я подчеркиваю, не определено спецификацией и, как таковое, зависит от реализации), это решение будет продолжать работать.

Здесь следует отметить одну забавную вещь *q : и arr оба указывают на одно и то же в памяти. Это означает, что вы можете делать забавные вещи, подобные этому:

 arr := [5]int{1, 2, 3, 4, 5}
q := (*MyStruct)(unsafe.Pointer(amp;arr))
fmt.Printf("%#vn", q) // => amp;main.MyStruct{A:1, B:2, C:3, D:4, E:5}
arr[0] = 0xFFFF
fmt.Printf("%#vn", q) // => amp;main.MyStruct{A:65535, B:2, C:3, D:4, E:5}
 

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

 // with the extra asterisk, called indirect,
// q will be type MyStruct and the example above will fail
q := *(*MyStruct)(unsafe.Pointer(amp;arr))
 

Наконец, более реальное решение, но неэффективное, если у вас нет структуры со многими полями (в этом случае вам, вероятно, следует просто использовать генерацию кода, и почему вы все равно используете такую большую структуру?)

 arr := [5]int{1, 2, 3, 4, 5}
v := reflect.ValueOf(amp;MyStruct{}).Elem()
for i, val := range arr {
    v.Field(i).SetInt(int64(val))
}
fmt.Printf("%#vn",v.Interface().(MyStruct))
 

Игровая площадка

Должны ли вы на самом деле использовать это? Не совсем, но это лучше, чем использовать unsafe, потому reflect что библиотека сохраняет обещание совместимости Go 1. Однако обратите внимание, что это будет самое медленное из всех решений, особенно после того, как вы добавите все проверки, которые вам понадобятся для реальной работы над этим в реальной жизни. Как указано выше, в общем случае, если у вас есть так много полей, чтобы оправдать использование reflect для сокращения вашего кода, вы должны сначала спросить себя, что хорошего в том, чтобы иметь такую большую структуру, и если у вас есть хороший ответ на это, то реальное решение — создать генератор кода, которыйвыполняет копирование массива в структуру.

(Смысл этого ответа состоит в том, чтобы показать, что да, действительно, есть способы, которыми вы действительно можете решить проблему OP, но есть и очень веские причины, по которым их не следует использовать!)