#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)))
}
Комментарии:
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() но это легко читается.