From 48b757cac1fb908555abf2864632d47d97e8fed3 Mon Sep 17 00:00:00 2001 From: Marcel Meyer Date: Mon, 26 May 2025 13:37:26 +0200 Subject: [PATCH] refactor: use slices package for sort (#10132) The sort package is still used in places that were not trivial to change. Since Go 1.21 slices package can be uswed for sort. See https://go.dev/doc/go1.21#slices ### Purpose Make some progress with the migration to a more up-to-date syntax. --- cmd/infra/stcrashreceiver/diskstore.go | 7 ++++--- cmd/syncthing/cli/index_dumpsize.go | 7 ++++--- cmd/syncthing/cli/index_idxck.go | 11 ++++++----- cmd/syncthing/main.go | 4 ++-- lib/api/api.go | 15 ++++++++------- lib/build/build.go | 4 ++-- lib/config/config.go | 5 +++-- lib/config/config_test.go | 4 ++-- lib/config/migrations.go | 9 +++++---- lib/config/versioningconfiguration.go | 7 ++++--- lib/connections/service.go | 3 +-- lib/db/smallindex.go | 4 ++-- lib/discover/manager.go | 4 ++-- lib/fs/basicfs_test.go | 13 ++++++------- lib/fs/basicfs_xattr_bsdish.go | 4 ++-- lib/fs/basicfs_xattr_linuxish.go | 4 ++-- lib/fs/casefs_test.go | 4 ++-- lib/fs/fakefs_test.go | 6 +++--- lib/model/folder.go | 3 ++- lib/model/model_test.go | 5 ++--- lib/relay/client/dynamic.go | 4 ++-- lib/syncthing/syncthing.go | 6 +++--- lib/ur/usage_report.go | 6 +++--- lib/versioner/simple.go | 4 ++-- lib/versioner/staggered.go | 4 ++-- lib/versioner/staggered_test.go | 6 +++--- lib/versioner/util.go | 4 ++-- script/transifexdl.go | 4 ++-- script/weblatedl.go | 4 ++-- 29 files changed, 85 insertions(+), 80 deletions(-) diff --git a/cmd/infra/stcrashreceiver/diskstore.go b/cmd/infra/stcrashreceiver/diskstore.go index 124d77d4a..fca0fdc01 100644 --- a/cmd/infra/stcrashreceiver/diskstore.go +++ b/cmd/infra/stcrashreceiver/diskstore.go @@ -8,6 +8,7 @@ package main import ( "bytes" + "cmp" "compress/gzip" "context" "io" @@ -15,7 +16,7 @@ import ( "math" "os" "path/filepath" - "sort" + "slices" "time" ) @@ -177,8 +178,8 @@ func (d *diskStore) inventory() error { }) return nil }) - sort.Slice(d.currentFiles, func(i, j int) bool { - return d.currentFiles[i].mtime < d.currentFiles[j].mtime + slices.SortFunc(d.currentFiles, func(a, b currentFile) int { + return cmp.Compare(a.mtime, b.mtime) }) var oldest time.Duration if len(d.currentFiles) > 0 { diff --git a/cmd/syncthing/cli/index_dumpsize.go b/cmd/syncthing/cli/index_dumpsize.go index da7a6dbe6..17cfa61c4 100644 --- a/cmd/syncthing/cli/index_dumpsize.go +++ b/cmd/syncthing/cli/index_dumpsize.go @@ -7,9 +7,10 @@ package cli import ( + "cmp" "encoding/binary" "fmt" - "sort" + "slices" "github.com/syncthing/syncthing/lib/db" ) @@ -77,8 +78,8 @@ func indexDumpSize() error { elems = append(elems, ele) } - sort.Slice(elems, func(i, j int) bool { - return elems[i].size > elems[j].size + slices.SortFunc(elems, func(a, b sizedElement) int { + return cmp.Compare(b.size, a.size) }) for _, ele := range elems { fmt.Println(ele.key, ele.size) diff --git a/cmd/syncthing/cli/index_idxck.go b/cmd/syncthing/cli/index_idxck.go index a0dab459a..4e1cbc181 100644 --- a/cmd/syncthing/cli/index_idxck.go +++ b/cmd/syncthing/cli/index_idxck.go @@ -8,10 +8,11 @@ package cli import ( "bytes" + "cmp" "encoding/binary" "errors" "fmt" - "sort" + "slices" "google.golang.org/protobuf/proto" @@ -207,11 +208,11 @@ func indexCheck() (err error) { // Aggregate the ranges of missing sequence entries, print them - sort.Slice(missingSeq, func(a, b int) bool { - if missingSeq[a].folder != missingSeq[b].folder { - return missingSeq[a].folder < missingSeq[b].folder + slices.SortFunc(missingSeq, func(a, b sequenceKey) int { + if a.folder != b.folder { + return cmp.Compare(a.folder, b.folder) } - return missingSeq[a].sequence < missingSeq[b].sequence + return cmp.Compare(a.sequence, b.sequence) }) var folder uint32 diff --git a/cmd/syncthing/main.go b/cmd/syncthing/main.go index 6a1185d98..458f0761e 100644 --- a/cmd/syncthing/main.go +++ b/cmd/syncthing/main.go @@ -23,7 +23,7 @@ import ( "path/filepath" "regexp" "runtime/pprof" - "sort" + "slices" "strconv" "strings" "syscall" @@ -443,7 +443,7 @@ func debugFacilities() string { maxLen = len(name) } } - sort.Strings(names) + slices.Sort(names) // Format the choices b := new(bytes.Buffer) diff --git a/lib/api/api.go b/lib/api/api.go index 8d07546b9..1115926cc 100644 --- a/lib/api/api.go +++ b/lib/api/api.go @@ -8,6 +8,7 @@ package api import ( "bytes" + "cmp" "context" "crypto/tls" "crypto/x509" @@ -24,7 +25,7 @@ import ( "reflect" "runtime" "runtime/pprof" - "sort" + "slices" "strconv" "strings" "time" @@ -750,7 +751,7 @@ func (*service) getSystemVersion(w http.ResponseWriter, _ *http.Request) { func (*service) getSystemDebug(w http.ResponseWriter, _ *http.Request) { names := l.Facilities() enabled := l.FacilityDebugging() - sort.Strings(enabled) + slices.Sort(enabled) sendJSON(w, map[string]interface{}{ "facilities": names, "enabled": enabled, @@ -1535,8 +1536,8 @@ func (*service) getLang(w http.ResponseWriter, r *http.Request) { langs = append(langs, code) } // Reorder by descending q value - sort.SliceStable(langs, func(i, j int) bool { - return weights[langs[i]] > weights[langs[j]] + slices.SortStableFunc(langs, func(i, j string) int { + return cmp.Compare(weights[j], weights[i]) }) sendJSON(w, langs) } @@ -1822,8 +1823,8 @@ func browseFiles(ffs fs.Filesystem, search string) []string { } // sort to return matches in deterministic order (don't depend on file system order) - sort.Strings(exactMatches) - sort.Strings(caseInsMatches) + slices.Sort(exactMatches) + slices.Sort(caseInsMatches) return append(exactMatches, caseInsMatches...) } @@ -1920,7 +1921,7 @@ func dirNames(dir string) []string { } } - sort.Strings(dirs) + slices.Sort(dirs) return dirs } diff --git a/lib/build/build.go b/lib/build/build.go index 098dd7c7c..a07682451 100644 --- a/lib/build/build.go +++ b/lib/build/build.go @@ -12,7 +12,7 @@ import ( "os" "regexp" "runtime" - "sort" + "slices" "strconv" "strings" "time" @@ -109,7 +109,7 @@ func TagsList() []string { tags = append(tags, Extra) } - sort.Strings(tags) + slices.Sort(tags) return tags } diff --git a/lib/config/config.go b/lib/config/config.go index 73c48ecd0..740054726 100644 --- a/lib/config/config.go +++ b/lib/config/config.go @@ -17,6 +17,7 @@ import ( "net/url" "os" "reflect" + "slices" "sort" "strconv" "strings" @@ -380,8 +381,8 @@ func (cfg *Configuration) prepareFolders(myID protocol.DeviceID, existingDevices } } // Ensure that the folder list is sorted by ID - sort.Slice(cfg.Folders, func(a, b int) bool { - return cfg.Folders[a].ID < cfg.Folders[b].ID + slices.SortFunc(cfg.Folders, func(a, b FolderConfiguration) int { + return strings.Compare(a.ID, b.ID) }) return sharedFolders, nil } diff --git a/lib/config/config_test.go b/lib/config/config_test.go index 27cfdd879..8031fadb9 100644 --- a/lib/config/config_test.go +++ b/lib/config/config_test.go @@ -17,7 +17,7 @@ import ( "path/filepath" "reflect" "runtime" - "sort" + "slices" "strings" "testing" @@ -911,7 +911,7 @@ func TestV14ListenAddressesMigration(t *testing.T) { t.Error("Configuration was not converted") } - sort.Strings(tc[2]) + slices.Sort(tc[2]) if !reflect.DeepEqual(cfg.Options.RawListenAddresses, tc[2]) { t.Errorf("Migration error; actual %#v != expected %#v", cfg.Options.RawListenAddresses, tc[2]) } diff --git a/lib/config/migrations.go b/lib/config/migrations.go index 4a4ecdf37..838d0154f 100644 --- a/lib/config/migrations.go +++ b/lib/config/migrations.go @@ -7,11 +7,12 @@ package config import ( + "cmp" "net/url" "os" "path" "path/filepath" - "sort" + "slices" "strings" "sync" @@ -65,8 +66,8 @@ type migrationSet []migration func (ms migrationSet) apply(cfg *Configuration) { // Make sure we apply the migrations in target version order regardless // of how it was defined. - sort.Slice(ms, func(a, b int) bool { - return ms[a].targetVersion < ms[b].targetVersion + slices.SortFunc(ms, func(a, b migration) int { + return cmp.Compare(a.targetVersion, b.targetVersion) }) // Apply all migrations. @@ -349,7 +350,7 @@ func migrateToConfigV14(cfg *Configuration) { cfg.Options.DeprecatedRelayServers = nil // For consistency - sort.Strings(cfg.Options.RawListenAddresses) + slices.Sort(cfg.Options.RawListenAddresses) var newAddrs []string for _, addr := range cfg.Options.RawGlobalAnnServers { diff --git a/lib/config/versioningconfiguration.go b/lib/config/versioningconfiguration.go index 988f8ab44..66327aa8e 100644 --- a/lib/config/versioningconfiguration.go +++ b/lib/config/versioningconfiguration.go @@ -9,7 +9,8 @@ package config import ( "encoding/json" "encoding/xml" - "sort" + "slices" + "strings" "github.com/syncthing/syncthing/lib/structutil" ) @@ -84,8 +85,8 @@ func (c *VersioningConfiguration) toInternal() internalVersioningConfiguration { for k, v := range c.Params { tmp.Params = append(tmp.Params, internalParam{k, v}) } - sort.Slice(tmp.Params, func(a, b int) bool { - return tmp.Params[a].Key < tmp.Params[b].Key + slices.SortFunc(tmp.Params, func(a, b internalParam) int { + return strings.Compare(a.Key, b.Key) }) return tmp } diff --git a/lib/connections/service.go b/lib/connections/service.go index be432d37e..f548d0e9f 100644 --- a/lib/connections/service.go +++ b/lib/connections/service.go @@ -23,7 +23,6 @@ import ( "net" "net/url" "slices" - "sort" "strings" stdsync "sync" "time" @@ -1151,7 +1150,7 @@ func (s *service) dialParallel(ctx context.Context, deviceID protocol.DeviceID, } // Sort the priorities so that we dial lowest first (which means highest...) - sort.Ints(priorities) + slices.Sort(priorities) sema := semaphore.MultiSemaphore{semaphore.New(dialMaxParallelPerDevice), parentSema} for _, prio := range priorities { diff --git a/lib/db/smallindex.go b/lib/db/smallindex.go index b426d906b..c53c69804 100644 --- a/lib/db/smallindex.go +++ b/lib/db/smallindex.go @@ -8,7 +8,7 @@ package db import ( "encoding/binary" - "sort" + "slices" "github.com/syncthing/syncthing/lib/db/backend" "github.com/syncthing/syncthing/lib/sync" @@ -147,6 +147,6 @@ func (i *smallIndex) Values() []string { } i.mut.Unlock() - sort.Strings(vals) + slices.Sort(vals) return vals } diff --git a/lib/discover/manager.go b/lib/discover/manager.go index 4cec90dce..4c12fbf31 100644 --- a/lib/discover/manager.go +++ b/lib/discover/manager.go @@ -13,7 +13,7 @@ import ( "context" "crypto/tls" "fmt" - "sort" + "slices" "time" "github.com/thejerf/suture/v4" @@ -159,7 +159,7 @@ func (m *manager) Lookup(ctx context.Context, deviceID protocol.DeviceID) (addre m.mut.RUnlock() addresses = stringutil.UniqueTrimmedStrings(addresses) - sort.Strings(addresses) + slices.Sort(addresses) l.Debugln("lookup results for", deviceID) l.Debugln(" addresses: ", addresses) diff --git a/lib/fs/basicfs_test.go b/lib/fs/basicfs_test.go index 66306f8f8..4d194f3ed 100644 --- a/lib/fs/basicfs_test.go +++ b/lib/fs/basicfs_test.go @@ -12,7 +12,7 @@ import ( "fmt" "os" "path/filepath" - "sort" + "slices" "strconv" "strings" "syscall" @@ -218,7 +218,7 @@ func TestDirNames(t *testing.T) { "a", "bC", } - sort.Strings(testCases) + slices.Sort(testCases) for _, sub := range testCases { if err := os.Mkdir(filepath.Join(dir, sub), 0o777); err != nil { @@ -229,7 +229,7 @@ func TestDirNames(t *testing.T) { if dirs, err := fs.DirNames("."); err != nil || len(dirs) != len(testCases) { t.Errorf("%s %s %s", err, dirs, testCases) } else { - sort.Strings(dirs) + slices.Sort(dirs) for i := range dirs { if dirs[i] != testCases[i] { t.Errorf("%s != %s", dirs[i], testCases[i]) @@ -321,8 +321,8 @@ func TestGlob(t *testing.T) { for _, testCase := range testCases { results, err := fs.Glob(testCase.pattern) - sort.Strings(results) - sort.Strings(testCase.matches) + slices.Sort(results) + slices.Sort(testCase.matches) if err != nil { t.Error(err) } @@ -628,8 +628,7 @@ func TestXattr(t *testing.T) { Value: value, }) } - sort.Slice(attrs, func(i, j int) bool { return attrs[i].Name < attrs[j].Name }) - + slices.SortFunc(attrs, func(a, b protocol.Xattr) int { return strings.Compare(a.Name, b.Name) }) // Set the xattrs, read them back and compare if err := tfs.SetXattr("/test", attrs, testXattrFilter{}); err != nil { t.Fatal(err) diff --git a/lib/fs/basicfs_xattr_bsdish.go b/lib/fs/basicfs_xattr_bsdish.go index a10c3ee3e..c22d2b73c 100644 --- a/lib/fs/basicfs_xattr_bsdish.go +++ b/lib/fs/basicfs_xattr_bsdish.go @@ -12,7 +12,7 @@ package fs import ( "errors" "fmt" - "sort" + "slices" "unsafe" "golang.org/x/sys/unix" @@ -69,7 +69,7 @@ func listXattr(path string) ([]string, error) { } } - sort.Strings(attrs) + slices.Sort(attrs) return attrs, nil } diff --git a/lib/fs/basicfs_xattr_linuxish.go b/lib/fs/basicfs_xattr_linuxish.go index 4857a4098..4df9921d8 100644 --- a/lib/fs/basicfs_xattr_linuxish.go +++ b/lib/fs/basicfs_xattr_linuxish.go @@ -12,7 +12,7 @@ package fs import ( "errors" "fmt" - "sort" + "slices" "strings" "golang.org/x/sys/unix" @@ -38,6 +38,6 @@ func listXattr(path string) ([]string, error) { buf = buf[:size] attrs := compact(strings.Split(string(buf), "\x00")) - sort.Strings(attrs) + slices.Sort(attrs) return attrs, nil } diff --git a/lib/fs/casefs_test.go b/lib/fs/casefs_test.go index 8bd78cb57..10329b36a 100644 --- a/lib/fs/casefs_test.go +++ b/lib/fs/casefs_test.go @@ -11,7 +11,7 @@ import ( "fmt" "path/filepath" "runtime" - "sort" + "slices" "strings" "testing" "time" @@ -344,7 +344,7 @@ func fakefsForBenchmark(nfiles int, latency time.Duration) (Filesystem, []string return nil, nil, errors.New("didn't find enough stuff") } - sort.Strings(paths) + slices.Sort(paths) return fsys, paths, nil } diff --git a/lib/fs/fakefs_test.go b/lib/fs/fakefs_test.go index 770461114..74d58eabe 100644 --- a/lib/fs/fakefs_test.go +++ b/lib/fs/fakefs_test.go @@ -14,7 +14,7 @@ import ( "path" "path/filepath" "runtime" - "sort" + "slices" "testing" "time" @@ -369,8 +369,8 @@ func assertDir(t *testing.T, fs Filesystem, directory string, filenames []string if path.Clean(directory) == "/" { filenames = append(filenames, ".stfolder") } - sort.Strings(filenames) - sort.Strings(got) + slices.Sort(filenames) + slices.Sort(got) if len(filenames) != len(got) { t.Errorf("want %s, got %s", filenames, got) diff --git a/lib/model/folder.go b/lib/model/folder.go index f8b8c501f..a0c342158 100644 --- a/lib/model/folder.go +++ b/lib/model/folder.go @@ -12,6 +12,7 @@ import ( "fmt" "math/rand" "path/filepath" + "slices" "sort" "time" @@ -1341,7 +1342,7 @@ func unifySubs(dirs []string, exists func(dir string) bool) []string { if len(dirs) == 0 { return nil } - sort.Strings(dirs) + slices.Sort(dirs) if dirs[0] == "" || dirs[0] == "." || dirs[0] == string(fs.PathSeparator) { return nil } diff --git a/lib/model/model_test.go b/lib/model/model_test.go index f7399e4d9..e14834464 100644 --- a/lib/model/model_test.go +++ b/lib/model/model_test.go @@ -18,7 +18,6 @@ import ( "path/filepath" "runtime/pprof" "slices" - "sort" "strconv" "strings" "sync" @@ -4092,8 +4091,8 @@ func equalStringsInAnyOrder(a, b []string) bool { if len(a) != len(b) { return false } - sort.Strings(a) - sort.Strings(b) + slices.Sort(a) + slices.Sort(b) for i := range a { if a[i] != b[i] { return false diff --git a/lib/relay/client/dynamic.go b/lib/relay/client/dynamic.go index baef98df3..8ab7d5ad2 100644 --- a/lib/relay/client/dynamic.go +++ b/lib/relay/client/dynamic.go @@ -10,7 +10,7 @@ import ( "fmt" "net/http" "net/url" - "sort" + "slices" "sync" "time" @@ -166,7 +166,7 @@ func relayAddressesOrder(ctx context.Context, input []string) []string { ids = append(ids, id) } - sort.Ints(ids) + slices.Sort(ids) addresses := make([]string, 0, len(input)) for _, id := range ids { diff --git a/lib/syncthing/syncthing.go b/lib/syncthing/syncthing.go index e43a04d25..733a095e7 100644 --- a/lib/syncthing/syncthing.go +++ b/lib/syncthing/syncthing.go @@ -15,7 +15,7 @@ import ( "net/http" "os" "runtime" - "sort" + "slices" "strings" "sync" "time" @@ -437,8 +437,8 @@ func printServiceTree(w io.Writer, sup supervisor, level int) { printService(w, sup, level) svcs := sup.Services() - sort.Slice(svcs, func(a, b int) bool { - return fmt.Sprint(svcs[a]) < fmt.Sprint(svcs[b]) + slices.SortFunc(svcs, func(a, b suture.Service) int { + return strings.Compare(fmt.Sprint(a), fmt.Sprint(b)) }) for _, svc := range svcs { diff --git a/lib/ur/usage_report.go b/lib/ur/usage_report.go index 21aea826e..c80168738 100644 --- a/lib/ur/usage_report.go +++ b/lib/ur/usage_report.go @@ -16,7 +16,7 @@ import ( "net/http" "os" "runtime" - "sort" + "slices" "strings" "sync" "time" @@ -162,7 +162,7 @@ func (s *Service) reportData(ctx context.Context, urVersion int, preview bool) ( l.Warnf("Unhandled versioning type for usage reports: %s", cfg.Versioning.Type) } } - sort.Ints(report.RescanIntvs) + slices.Sort(report.RescanIntvs) for _, cfg := range s.cfg.Devices() { if cfg.Introducer { @@ -295,7 +295,7 @@ func (s *Service) reportData(ctx context.Context, urVersion int, preview bool) ( report.FolderUsesV3.SyncOwnership++ } } - sort.Ints(report.FolderUsesV3.FsWatcherDelays) + slices.Sort(report.FolderUsesV3.FsWatcherDelays) for _, cfg := range s.cfg.Devices() { if cfg.Untrusted { diff --git a/lib/versioner/simple.go b/lib/versioner/simple.go index f56a8279b..24b861346 100644 --- a/lib/versioner/simple.go +++ b/lib/versioner/simple.go @@ -8,7 +8,7 @@ package versioner import ( "context" - "sort" + "slices" "strconv" "time" @@ -79,7 +79,7 @@ func (v simple) toRemove(versions []string, now time.Time) []string { var remove []string // The list of versions may or may not be properly sorted. - sort.Strings(versions) + slices.Sort(versions) // If the amount of elements exceeds the limit: the oldest elements are to be removed. if len(versions) > v.keep { diff --git a/lib/versioner/staggered.go b/lib/versioner/staggered.go index e97c2e6d4..9bb56488a 100644 --- a/lib/versioner/staggered.go +++ b/lib/versioner/staggered.go @@ -9,7 +9,7 @@ package versioner import ( "context" "fmt" - "sort" + "slices" "strconv" "time" @@ -69,7 +69,7 @@ func (v *staggered) toRemove(versions []string, now time.Time) []string { var remove []string // The list of versions may or may not be properly sorted. - sort.Strings(versions) + slices.Sort(versions) for _, version := range versions { versionTime, err := time.ParseInLocation(TimeFormat, extractTag(version), time.Local) diff --git a/lib/versioner/staggered_test.go b/lib/versioner/staggered_test.go index 534705599..0e6623b80 100644 --- a/lib/versioner/staggered_test.go +++ b/lib/versioner/staggered_test.go @@ -9,7 +9,7 @@ package versioner import ( "os" "path/filepath" - "sort" + "slices" "strconv" "testing" "time" @@ -97,7 +97,7 @@ func TestStaggeredVersioningVersionCount(t *testing.T) { "test~20150416-135958", // 365 days 2 seconds ago "test~20150414-140000", // 367 days ago } - sort.Strings(delete) + slices.Sort(delete) cfg := config.FolderConfiguration{ FilesystemType: config.FilesystemTypeBasic, @@ -111,7 +111,7 @@ func TestStaggeredVersioningVersionCount(t *testing.T) { v := newStaggered(cfg).(*staggered) rem := v.toRemove(versionsWithMtime, now) - sort.Strings(rem) + slices.Sort(rem) if diff, equal := messagediff.PrettyDiff(delete, rem); !equal { t.Errorf("Incorrect deleted files; got %v, expected %v\n%v", rem, delete, diff) diff --git a/lib/versioner/util.go b/lib/versioner/util.go index 66d8c90ba..070cdf5bb 100644 --- a/lib/versioner/util.go +++ b/lib/versioner/util.go @@ -13,7 +13,7 @@ import ( "os" "path/filepath" "regexp" - "sort" + "slices" "strings" "time" @@ -339,7 +339,7 @@ func findAllVersions(fs fs.Filesystem, filePath string) []string { return nil } versions = stringutil.UniqueTrimmedStrings(versions) - sort.Strings(versions) + slices.Sort(versions) return versions } diff --git a/script/transifexdl.go b/script/transifexdl.go index abb5291cf..bd7ee79e1 100644 --- a/script/transifexdl.go +++ b/script/transifexdl.go @@ -17,7 +17,7 @@ import ( "net/http" "os" "regexp" - "sort" + "slices" "strings" ) @@ -93,7 +93,7 @@ func main() { } func saveValidLangs(langs []string) { - sort.Strings(langs) + slices.Sort(langs) fd, err := os.Create("valid-langs.js") if err != nil { log.Fatal(err) diff --git a/script/weblatedl.go b/script/weblatedl.go index 692cca11e..867ca9d2c 100644 --- a/script/weblatedl.go +++ b/script/weblatedl.go @@ -17,7 +17,7 @@ import ( "net/http" "os" "regexp" - "sort" + "slices" "strings" ) @@ -116,7 +116,7 @@ func reformatLanguageCode(origCode string) string { } func saveValidLangs(langs []string) { - sort.Strings(langs) + slices.Sort(langs) fd, err := os.Create("valid-langs.js") if err != nil { log.Fatal(err)