Глубокое копирование структур данных в Голанге - PullRequest
1 голос
/ 29 мая 2019

Я хочу продублировать экземпляр структуры данных. Поскольку у go нет встроенных функций, я использую стороннюю библиотеку: https://github.com/emirpasic/gods.

Например, я могу попробовать использовать глубокое копирование с хэш-набором.

var c, d hashset.Set
c = *hashset.New()
c.Add(1)
deepcopy.Copy(d, c)
c.Add(2)
fmt.Println(c.Contains(2))
fmt.Println(d.Contains(2))
fmt.Println(c.Contains(1))
fmt.Println(d.Contains(1))

Однако содержимое набора хэшей вообще не копируется. Я знаю, что модули глубокого копирования не могут копировать неэкспортированные значения, но поскольку в библиотеке нет встроенного «конструктора копирования», значит ли это, что невозможно полностью продублировать экземпляр структуры данных с библиотекой без изменения ее кода? (Аналогичная проблема возникает с некоторыми другими библиотеками, которые я изучал).

Я новичок в golang и не чувствую себя хорошо, поскольку подобные вещи легко достижимы, например, в C ++. Я знаю, что мог бы написать свою собственную версию или изменить их код, но это слишком много работы, чем ожидалось, и поэтому я думаю, что должен быть идиоматический способ.

PS: Для людей, которые могут сказать «нет необходимости в такой функциональности», я распределяю некоторые сложные состояния с некоторыми структурами данных для параллельных потоков вычислений, они используют состояния напрямую и не должны мешать друг другу.

Ответы [ 2 ]

1 голос
/ 29 мая 2019

К сожалению или нет, в Go нет способа сделать это. Первый инструмент, который приходит на ум, - это отражение (пакет reflect), но используя отражение, вы можете только читать неэкспортированные поля, но не можете их установить. См. Как клонировать структуру с неэкспортированным полем?

Единственный способ клонировать структуры с неэкспортированными полями - использовать пакет unsafe (см. Пример здесь: Доступ к неэкспортированным полям в golang / рефлексе? ), но как его имя говорит: это небезопасно , и вы должны держаться от него подальше, насколько это возможно. Программы, созданные с использованием unsafe, не гарантируют, что они продолжат работать с более новыми выпусками Go или будут вести себя одинаково на всех платформах.

В общем случае единственный и правильный способ поддержки клонирования в Go - это если сам пакет поддерживает такие операции.

Примечание № 1:

Это не означает, что в некоторых конкретных случаях вы не можете «имитировать» клонирование, создавая новое значение и создавая его состояние вручную. Например, вы можете клонировать map, создав новую карту, итерируя по парам ключ-значение оригинала и установив их на новой карте.

Примечание № 2:

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

Как в этом примере:

type person struct {
    Name string
    age  *int
}

age := 22
p := &person{"Bob", &age}
fmt.Println(p)

p2 := new(person)
*p2 = *p
fmt.Println(p2)

Что будет выводиться (попробуйте на Go Playground ):

&{Bob 0x414020}
&{Bob 0x414020}

Что мы можем обобщить, используя reflect, не полагаясь на конкретный тип:

type person struct {
    Name string
    age  *int
}

age := 22
p := &person{"Bob", &age}
fmt.Println(p)

v := reflect.ValueOf(p).Elem()
vp2 := reflect.New(v.Type())
vp2.Elem().Set(v)
fmt.Println(vp2)

Попробуйте это на Go Playground .

Но то, что мы не можем сделать , это изменить неэкспортированное поле person.age, чтобы оно указывало на что-то еще. Без помощи объявленного пакета это может быть только nil или то же значение указателя (указывающее на объект как исходное поле).

См. Также: Более быстрый способ глубокой копии объектов в Голанге

0 голосов
/ 29 мая 2019

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

В вашем примере:

func NewHashSetCopy(src *hashset.Set) *hashset.Set{
    dst := *hashset.New()
    iterator := src.Iter()
    for elem := range iterator {
        dst.Add(elem)
    }
    return dst
}
...