strings.Title 関数は非推奨になった

たとえば,以下のコードがあるとする。

package main

import (
    "fmt"
    "strings"
)

func main() {
    msgs := []string{
        "hello, world!",
        "HELLO, WORLD!",
    }
    for _, msg := range msgs {
        fmt.Println(msg, "->", strings.Title(msg))
    }
}

これを実行すると

$ go run sample1.go
hello, world! -> Hello, World!
HELLO, WORLD! -> HELLO, WORLD!

と単語の先頭が大文字に変換される。

この strings.Title() 関数について Go 1.18 から

The Title function is now deprecated. It doesn’t handle Unicode punctuation and language-specific capitalization rules, and is superseded by the golang.org/x/text/cases package.

という感じに非推奨になった。 なお,このコードを golangci-lint にかけると

$ golangci-lint run
sample1.go:14:26: SA1019: strings.Title has been deprecated since Go 1.18 and an alternative has been available since Go 1.0: The rule Title uses for word boundaries does not handle Unicode punctuation properly. Use golang.org/x/text/cases instead. (staticcheck)
        fmt.Println(msg, "->", strings.Title(msg))
                               ^

と言う感じに警告を出してくれる。 つか,この警告で気がついたんだけどね。 相変わらず lint に叱られっぱなしである orz

ちなみに似た関数名に strings.ToTitle() というのがあるが,これを使うと

$ go run sample1b.go
hello, world! -> HELLO, WORLD!
HELLO, WORLD! -> HELLO, WORLD!

という感じに全部大文字になる。 これって strings.ToUpper() と何が違うんか分からん(笑)

さて, strings.Title() 関数を使う代わりに golang.org/x/text/cases を使えとあるようなので,早速コードを書き換えてみる。 こんな感じかな。

package main

import (
    "fmt"

    "golang.org/x/text/cases"
    "golang.org/x/text/language"
)

func main() {
    msgs := []string{
        "hello, world!",
        "HELLO, WORLD!",
    }
    for _, msg := range msgs {
        fmt.Println(msg, "->", cases.Title(language.Und).String(msg))
    }
}

これを実行すると

$ go run sample2.go
hello, world! -> Hello, World!
HELLO, WORLD! -> Hello, World!

おっと! 単語の2文字目以降が違うか。 じゃぁ,オプションをつけて

fmt.Println(msg, "->", cases.Title(language.Und, cases.NoLower).String(msg))

とすればいいのかな。

$ go run sample2b.go
hello, world! -> Hello, World!
HELLO, WORLD! -> HELLO, WORLD!

よーし,うむうむ,よーし。

language.Und の部分は特定の言語(language.Japanese とか)を指定できるのだが,たとえば役物が絡む場合:

package main

import (
    "fmt"

    "golang.org/x/text/cases"
    "golang.org/x/text/language"
)

func main() {
    msg := "'n"
    casers := []cases.Caser{
        cases.Title(language.English),
        cases.Title(language.Dutch),
    }
    for _, caser := range casers {
        fmt.Println(msg, "->", caser.String(msg))
    }
}

これを実行すると

$ go run sample3.go
'n -> 'N
'n -> 'n

という感じに言語によって違いが出るようだ?

Lint は用法・用量を守って正しく使いましょう

話は変わるが,「失敗」には大きく2つある。 「受動的失敗」と「能動的失敗」だ。 このフレーズは Bruce Schneier さんの『セキュリティはなぜやぶられたのか(Beyond Fear)』にセキュリティ用語として出てくる。

セキュリティシステムの問題は大きく分けてふたつのパターンがある。 ひとつ目は攻撃に対する防御が失敗するもの。 (中略) とるべき対策が実行されない受動的な失敗である。 ふたつ目は間違ったときに対策を実行して防御が失敗するもの。 (中略) とるべき対策を実行したがゆえの能動的な失敗である。

受動的失敗は批判の対象になりやすいが,受動的失敗を恐れるあまり能動的失敗に陥るというのもありがちなパターンである。

いや Twitter の TL で golangci-lint をフルオプションで起動して山ほど警告が出てきても平気みたいな記述を見かけたので。 これって典型的な能動的失敗だよなぁ。 オオカミが来た!

もとより lint のような静的コード解析は万能ではない。 なれば,より気にすべきなのは受動的失敗より能動的失敗である。

golangci-lint はオプション無しでもかなりの部分を網羅できる。 せいぜい gosec を追加するくらい。 まずはこれで警告が出ないことを目指した上で,プロダクトによって過不足があれば少しずつ調整していけばいいのだ。

たとえば,拙作の gnkf は MD5 や SHA-1 のハッシュ値を出力する機能があるが gosec を含めて lint をかけると「弱いハッシュ関数を使うな(←超意訳)」みたいな警告が出てしまう。 そこで

$ golangci-lint run --enable gosec --exclude "G501|G505" ./...

のようにチェック対象を調整している。 仕事で使うならメンバ間で .golangci.yaml 等を使って設定を合わせておけばいいだろう。

lint は用法・用量を守って正しく使いましょう。

ブックマーク

参考図書

photo
プログラミング言語Go
アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)
丸善出版 2016-06-20 (Release 2021-07-13)
Kindle版
B099928SJD (ASIN)
評価     

Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想はこちら

reviewed by Spiegel on 2021-05-22 (powered by PA-APIv5)

photo
セキュリティはなぜやぶられたのか
ブルース・シュナイアー (著), 井口 耕二 (翻訳)
日経BP 2007-02-15
単行本
4822283100 (ASIN), 9784822283100 (EAN), 4822283100 (ISBN)
評価     

原書のタイトルが “Beyond Fear: Thinking Sensibly About Security in an Uncertain World” なのに対して日本語タイトルがどうしようもなくヘボいが中身は名著。とりあえず読んどきなはれ。ゼロ年代当時 9.11 およびその後の米国のセキュリティ政策と深く関連している内容なので,そのへんを加味して読むとよい。

reviewed by Spiegel on 2019-02-11 (powered by PA-APIv5)