Golang: используйте string.join для псевдонима типа для строки

#go

#Вперед

Вопрос:

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

введите строку SpecialScopes

и я хочу присоединиться к массиву этого типа, используя строки.Функция объединения

 func MergeScopes(scopes ...SpecialScopes) SpecialScopes {
    return strings.Join(scopes, ",")
}
  

Но с приведенным выше я получаю ошибки

 cannot use scopes (type []SpecialScopes) as type []string in argument to strings.Join
cannot use strings.Join(scopes, ",") (type string) as type SpecialScopes in return argument
  

Есть ли способ заставить golang понять, что SpecialScopes — это просто другое имя для строк, и выполнить для него функцию объединения?
Если нет, то какой наиболее эффективный способ сделать это? Один из способов, который я вижу, — преобразовать все элементы массива в string, join, затем преобразовать его обратно в SpecialScopes и вернуть значение

Обновление 1: у меня есть рабочая реализация, которая приводит значения. Есть предложения по более быстрому способу сделать это?

 func MergeScopes(scopes ...SpecialScopes) SpecialScopes {
    var s []string
    for _, scope := range scopes {
        s = append(s, string(scope))
    }

    return SpecialScopes(strings.Join(s, ","))
}
  

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

1. Ответы верны для поставленного вопроса, и из них я бы выбрал Яндри, поскольку он не использует unsafe, но в целом я бы не беспокоился о скорости этой операции. Скорее всего, это не то место, где ваше приложение проводит большую часть времени, и даже в рамках этой функции выделение для построения объединенной строки может затмить стоимость создания промежуточного []string звена в вашем примере кода.

Ответ №1:

В основном это самый быстрый способ без использования unsafe .

 func MergeScopes(scopes ...SpecialScopes) SpecialScopes {
    if len(scopes) == 0 {
        return ""
    }
    var (
        sep = []byte(", ")
        // preallocate for len(sep)   assume at least 1 character
        out = make([]byte, 0, (1 len(sep))*len(scopes))
    )
    for _, s := range scopes {
        out = append(out, s...)
        out = append(out, sep...)
    }
    return SpecialScopes(out[:len(out)-len(sep)])
}
  

Тестовый код: https://play.golang.org/p/DrB8nM-6ws

 ━➤ go test -benchmem -bench=.  -v -benchtime=2s
testing: warning: no tests to run
BenchmarkUnsafe-8       30000000               109 ns/op              32 B/op          2 allocs/op
BenchmarkBuffer-8       20000000               255 ns/op             128 B/op          2 allocs/op
BenchmarkCopy-8         10000000               233 ns/op             112 B/op          3 allocs/op
BenchmarkConcat-8       30000000               112 ns/op              32 B/op          2 allocs/op
  

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

1. Спасибо! Анализ и сравнение действительно помогли

Ответ №2:

Вот решение с использованием unsafe пакета:

 func MergeScopes(scopes ...SpecialScopes) SpecialScopes {
    specialScopes := *(*[]string)((unsafe.Pointer(amp;scopes)))
    s := strings.Join(specialScopes, ",")
    return *(*SpecialScopes)((unsafe.Pointer(amp;s)))
}
  

https://play.golang.org/p/-wsHY2eCdc

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

1. Не неточно, но я сдержан, чтобы указать кому-то на unsafe, когда мы не установили, что хуже просто снизить затраты на производительность при создании a []string (или снизить затраты на строки кода, чтобы сделать это способом Яндри). Обратите внимание, например, если SpecialScopes изменяется с string на []byte или что-то еще, это все равно компилируется, но имеет неопределенное поведение. Мнения будут отличаться, просто отметив мое.

Ответ №3:

Если вы действительно хотите что-то быстрое, это более быстрый способ в Go:

 func MergeScopes(scopes ...SpecialScopes) SpecialScopes {
    var buffer bytes.Buffer
    for ix, s := range scopes {
        buffer.WriteString(string(s))
        if ix < len(scopes)-1 {
            buffer.WriteString(", ")    
        }
    }

    return SpecialScopes(buffer.String())
}
  

Полный пример: https://play.golang.org/p/McWjh1yxHf

Я знаю, что это не выглядит красиво, как простое.Join() но это легко читается.