При объединении двух фрагментов, например. фрукты и овощи, чтобы создать треть, например. еда. казалось бы, имеет смысл добавить фрукты и овощи и приписать результат непосредственно еде.
Проверка golangci-lint: «appendAssign» предполагает, что это неприемлемый способ, альтернатива (см. ниже) предпочтительнее.
Каков наиболее идиоматический способ добиться этого?
Код для иллюстрации вопроса: https://go.dev/play/p/IFV6o92-HTb
package main
import "fmt"
func main() {
// fails linting with: gocritic: appendAssign: append result not assigned to the same slice
fruits := []string{"banana", "strawberry"}
vegetables := []string{"potato", "carrot"}
food := append(fruits, vegetables...)
fmt.Println(food)
// reset
food = nil
// passes linting
food = fruits
food = append(food, vegetables...)
fmt.Println(food)
}
Обсуждение здесь: github.com/go-critic/go-critic/issues/865 . Я не поклонник предупреждений линтера, которые имеют такие ложные срабатывания.
Да, вы должны быть осторожны с добавлением и случайным псевдонимированием резервных массивов, но это предупреждение о ворсе не является хорошим решением для предотвращения этого.
Одной из причин может быть то, что вы можете получить удивительные результаты, например (Go Playground):
func main() {
fruits := []string{"banana", "strawberry", "apple", "cherry"}
vegetables := []string{"potato", "carrot"}
food := append(fruits[:2], vegetables...)
fmt.Println(food) // [banana strawberry potato carrot]
fmt.Println(fruits) // [banana strawberry potato carrot]
}
или даже
func main() {
allFruits := []string{"banana", "strawberry", "apple", "cherry"}
fruits := allFruits[:2]
vegetables := []string{"potato", "carrot"}
food := append(fruits, vegetables...)
fmt.Println(food) // [banana strawberry potato carrot]
fmt.Println(allFruits) // [banana strawberry potato carrot]
}
потому что вы потенциально изменяете базовый массив первого среза, который может быть легко пропущен обычным читателем. Это сразу становится ясно, когда вы присваиваете одну и ту же переменную на одном и том же шаге.
Лучше всего использовать срезы.Конкат:
food := slices.Concat(fruits, vegetables)
альтернативой будет:
food := make([]string, 0, len(fruits)+len(vegetables))
food = append(food, fruits...)
food = append(food, vegetables...)
имея преимущество предварительно выделенного массива. Также возможно:
var food []string
food = append(food, fruits...)
food = append(food, vegetables...)
Хотя то, что вы делаете, нормально с точки зрения языка, это может удивить людей, быстро читающих ваш код.
Вы можете не согласиться и игнорировать предупреждение о линтере или отключить его. По моему мнению, альтернативные подходы гораздо более читабельны, особенно slices.Concat
.
Возможно, это только я, но когда вы предлагали food := slices.Concat(fruits, vegetables)
, я ошибочно ожидал, что slices.Concat
не изменит базовый фрагмент, поэтому food := slices.Concat(fruits[:2], vegetables)
будет «безопасная» альтернатива, оставляющая fruits
нетронутым при копировании.
slices.Concat
не изменяет базовый массив он возвращает новый фрагмент, как указано в документации. slices.Concat(fruits[:2], vegetables)
безопасно.
Ох, моя вина! Шутка ли, я даже на игровой площадке тестировал и не туда посмотрел результат.
В этом коде нет ничего плохого. Я не понимаю, почему было создано это предупреждение о линтере.