Description
Proposal Details
I propose, a second time, the following language change first proposed for go1.5 in #9367:
An explicit conversion int(b)
, where b
has a boolean type and int
stands for any integral type, would become legal and would yield 1 if b
is true, 0 otherwise.
Rationale: currently this function cannot be computed in a single expression, despite it being often useful, hard to misuse, present in nearly all other languages, and syntactically natural in Go. As a corollary, there is currently no simple way to derive an integer constant from a boolean constant.
While I appreciate the value of avoiding implicit int-to-bool conversions, forbidding explicit ones seems slightly obtuse, and as a result of its omission, one must write four extra lines of code:
var i int
if b {
i = 1
}
... i ...
Also, consider implementations of sort.Interface.Less that define a total order over tuples. They consist of a sequence of operations, one per column (x, etc) of the tuple, of this form:
if a[i].x != a[j].x {
return a[i].x < a[j].x
}
For booleans, the <
operator must be expressed as the opaque !a[i].x && a[j].x
. It would be easier to read int(a[i].x) < int(a[j].x)
.
The motive here is brevity and clarity, not efficiency. I am confident that a good compiler can avoid the branch. The concern with efficiency in the previous proposal was a distraction, and it has been dealt with separately.
We do not propose to support the reverse operation, bool(i)
, as one can already say i != 0
in expression context (and it's shorter). Also, i != 0
makes the semantics explicit, where bool(i)
could potentially be misinterpreted as i > 0
, i == 1
, i == max[u]int
, i == -1
, or i & 1 == 1
, all of which are representations of "true" used in other languages; and George Boole would have argued that the bool(int) function is simply not defined for values other than zero and one.
We also do not propose to support float64(b)
and complex128(b)
. Though consistent, there is little need for such conversions, and they can easily be implemented in two steps, still within expression context, for example float64(int(b))
.
In https://go.dev/cl/550235, I measured the occurrence of code snippets like the one above. Across about 19,000 modules, there are about 10K places where an if/else statement is used to simulate an int(bool) conversion. (I didn't attempt to find places where if/else was used to compute bool < bool, as is common in sort.Interface.Less implementations.) Of those, I ignored the ~5.5K in generated .pb.go files, and the ~2K in copies of github.com/wzzhu/tensor, leaving about 2700 more interesting cases. Here's a random sample of a few dozen:
https://go-mod-viewer.appspot.com/[email protected]/cmd/scollector/collectors/ntp_unix.go#L76: [if]: current_source = int(fl == "")
https://go-mod-viewer.appspot.com/cosmossdk.io/client/[email protected]/internal/testpb/query.pulsar.go#L2731: [if-else]: dAtA[i] = int(x.Bools[iNdEx])
https://go-mod-viewer.appspot.com/github.com/BurntSushi/[email protected]/randr/randr.go#L3797: [if-else]: buf[b] = int(Delete)
https://go-mod-viewer.appspot.com/github.com/BurntSushi/[email protected]/xproto/xproto.go#L10065: [if-else]: buf[b] = int(Delete)
https://go-mod-viewer.appspot.com/github.com/BurntSushi/[email protected]/xproto/xproto.go#L10719: [if-else]: buf[b] = int(OwnerEvents)
https://go-mod-viewer.appspot.com/github.com/BurntSushi/[email protected]/xproto/xproto.go#L3668: [if-else]: buf[b] = int(v.OverrideRedirect)
https://go-mod-viewer.appspot.com/github.com/Hyperledger-TWGC/[email protected]/sm4/sm4_gcm.go#L77: [return]: return int(temp&0x01 == 1)
https://go-mod-viewer.appspot.com/github.com/XiaoMi/[email protected]/mysql/resultset_sort.go#L132: [return]: return int(v > s)
https://go-mod-viewer.appspot.com/github.com/andybalholm/[email protected]/brotli_bit_stream.go#L306: [if-else]: tmp = int(depths[symbols[0]] == 1)
https://go-mod-viewer.appspot.com/github.com/andybalholm/[email protected]/decode.go#L1282: [if-else]: s.should_wrap_ringbuffer = int(uint(s.pos) != 0)
https://go-mod-viewer.appspot.com/github.com/apache/arrow/go/[email protected]/arrow/compute/internal/exec/span.go#L280: [if-else]: a.Nulls = int(val.IsValid())
https://go-mod-viewer.appspot.com/github.com/apache/arrow/go/[email protected]/internal/bitutils/bitmap_generate.go#L85: [if-else]: outResults[i] = int(g())
https://go-mod-viewer.appspot.com/github.com/aviddiviner/[email protected]/docopt.go#L433: [if]: argcount = int(eq == "=")
https://go-mod-viewer.appspot.com/github.com/blevesearch/[email protected]/s2/edge_clipping.go#L148: [if]: ai = int(a.X > b.X)
https://go-mod-viewer.appspot.com/github.com/btccom/go-micro/[email protected]/api/router/static/static.go#L232: [if]: idx = int(len(req.URL.Path) > 0 && req.URL.Path != "/")
https://go-mod-viewer.appspot.com/github.com/cockroachdb/[email protected]/sstable/writer_test.go#L923: [if]: expectedLength = int(expectedIntersects)
https://go-mod-viewer.appspot.com/github.com/consensys/[email protected]/ecc/bw6-761/fr/fri/fri.go#L309: [if-else]: fullMerkleProof = int(len(pp.Rounds[0].Interactions[0][0].ProofSet) > len(pp.Rounds[0].Interactions[0][1].ProofSet))
https://go-mod-viewer.appspot.com/github.com/coreservice-io/[email protected]/version_util/version.go#L81: [return]: return int(a.tail > b.tail)
https://go-mod-viewer.appspot.com/github.com/cosmos/[email protected]/internal/testprotos/test3/test.pulsar.go#L6416: [if-else]: dAtA[i] = int(x.RepeatedBool[iNdEx])
https://go-mod-viewer.appspot.com/github.com/datastax/[email protected]/datacodec/boolean.go#L187: [if-else]: *d = int(wasNull || !val)
https://go-mod-viewer.appspot.com/github.com/dop251/[email protected]/ftoa/ftoa.go#L614: [if]: stop = int(len(buf) > 0 && buf[0] == '-')
https://go-mod-viewer.appspot.com/github.com/dotcloud/[email protected]/auto.go#L174: [if]: start = int(mtype.NumIn() > 0 && mtype.In(0).AssignableTo(reflect.TypeOf(autoHandler)))
https://go-mod-viewer.appspot.com/github.com/dubbogo/[email protected]/time/timer.go#L118: [if-else]: ret = int(first.trig > second.trig)
https://go-mod-viewer.appspot.com/github.com/fraugster/[email protected]/type_boolean.go#L91: [if]: v = int(values[i].(bool))
https://go-mod-viewer.appspot.com/github.com/gagliardetto/[email protected]/encoder.go#L189: [if]: num = int(b)
https://go-mod-viewer.appspot.com/github.com/golang-commonmark/[email protected]/blockquote.go#L160: [if]: d = int(spaceAfterMarker)
https://go-mod-viewer.appspot.com/github.com/hyperledger/[email protected]/core/FP256BN/FP4.go#L214: [if-else]: u = int(F.a.iszilch())
https://go-mod-viewer.appspot.com/github.com/intel/[email protected]/pkg/blockio/oci_test.go#L95: [if]: expectedErrorCount = int(len(tc.expectedErrorSubstrings) > 0)
https://go-mod-viewer.appspot.com/github.com/intel/[email protected]/pkg/sst/sst_if.go#L109: [if]: ReadWrite = int(doWrite)
https://go-mod-viewer.appspot.com/github.com/jezek/[email protected]/xprint/xprint.go#L639: [if-else]: buf[b] = int(Cancel)
https://go-mod-viewer.appspot.com/github.com/jezek/[email protected]/xproto/xproto.go#L7364: [if-else]: buf[b] = int(DoAcceleration)
https://go-mod-viewer.appspot.com/github.com/la5nta/[email protected]/rigcontrol/hamlib/rigctld.go#L168: [if]: bInt = int(on == true)
https://go-mod-viewer.appspot.com/github.com/m3db/[email protected]/src/dbnode/persist/fs/msgpack/stream.go#L170: [if]: unreadBytes = int(s.unreadByte != -1)
https://go-mod-viewer.appspot.com/github.com/m3db/[email protected]/src/dbnode/storage/bootstrap_instrumentation.go#L180: [if]: status = int(isBootstrapped)
https://go-mod-viewer.appspot.com/github.com/mitchellh/[email protected]/pixel_format.go#L111: [if-else]: boolByte = int(format.TrueColor)
https://go-mod-viewer.appspot.com/github.com/nicocha30/[email protected]/pkg/atomicbitops/bool.go#L34: [if]: u = int(val)
https://go-mod-viewer.appspot.com/github.com/nicocha30/[email protected]/pkg/state/wire/wire.go#L88: [if-else]: v = int(b)
https://go-mod-viewer.appspot.com/github.com/oasisprotocol/[email protected]/internal/modm/modm_64bit.go#L736: [if]: cmp = int(i == 0)
https://go-mod-viewer.appspot.com/github.com/onosproject/onos-api/[email protected]/onos/config/v3/typedvalue.go#L877: [if]: intval = int(b)
https://go-mod-viewer.appspot.com/github.com/parquet-go/[email protected]/row_buffer_test.go#L146: [if]: definitionLevel = int(!node.Required())
https://go-mod-viewer.appspot.com/github.com/peske/[email protected]/cmd/goyacc/yacc.go#L2126: [if]: nolook = int(tystate[i] != MUSTLOOKAHEAD)
https://go-mod-viewer.appspot.com/github.com/pion/webrtc/[email protected]/atomicbool.go#L27: [if]: i = int(value)
https://go-mod-viewer.appspot.com/github.com/pocketbase/[email protected]/forms/admin_upsert_test.go#L160: [if]: expectInterceptorCall = int(s.expectError)
https://go-mod-viewer.appspot.com/github.com/polarismesh/[email protected]/store/mysql/strategy.go#L80: [if]: isDefault = int(strategy.Default)
https://go-mod-viewer.appspot.com/github.com/regen-network/regen-ledger/api/[email protected]/regen/ecocredit/marketplace/v1/state.pulsar.go#L515: [if-else]: dAtA[i] = int(x.Maker)
https://go-mod-viewer.appspot.com/github.com/regen-network/regen-ledger/[email protected]/regen/ecocredit/marketplace/v1/tx.pulsar.go#L2785: [if-else]: dAtA[i] = int(x.DisableAutoRetire)
https://go-mod-viewer.appspot.com/github.com/regen-network/regen-ledger/[email protected]/regen/ecocredit/v1alpha1/types.pulsar.go#L2460: [if-else]: dAtA[i] = int(x.AllowlistEnabled)
https://go-mod-viewer.appspot.com/github.com/robotn/[email protected]/glx/glx.go#L1494: [if-else]: buf[b] = int(IsDirect)
https://go-mod-viewer.appspot.com/github.com/robotn/[email protected]/randr/randr.go#L3804: [if-else]: buf[b] = int(Pending)
https://go-mod-viewer.appspot.com/github.com/robotn/[email protected]/shape/shape.go#L130: [if-else]: buf[b] = int(v.Shaped)
https://go-mod-viewer.appspot.com/github.com/robotn/[email protected]/xproto/xproto.go#L10719: [if-else]: buf[b] = int(OwnerEvents)
https://go-mod-viewer.appspot.com/github.com/robotn/[email protected]/xproto/xproto.go#L7614: [if-else]: buf[b] = int(Exposures)
https://go-mod-viewer.appspot.com/github.com/sajari/[email protected]/fuzzy.go#L255: [if-else]: temp = int((*a)[j-1] == (*b)[i-1])
https://go-mod-viewer.appspot.com/github.com/segmentio/[email protected]/file.go#L692: [if]: f.index = int(f.dictOffset > 0)
https://go-mod-viewer.appspot.com/github.com/shakinm/[email protected]/xls/workbook.go#L107: [if-else]: grbitOffset = int(len(wb.sst.RgbSrc) == 0)
https://go-mod-viewer.appspot.com/github.com/thanos-io/[email protected]/pkg/store/cache/caching_bucket_test.go#L442: [if]: expectedHitsDiff = int(expectedCache)
https://go-mod-viewer.appspot.com/github.com/u2takey/[email protected]/strings/string.go#L225: [if]: i = int(strings.Index(str, \u) > 0)
https://go-mod-viewer.appspot.com/github.com/uadmin/[email protected]/setting.go#L522: [if]: n = int(v)
https://go-mod-viewer.appspot.com/github.com/zhangzhaojian/[email protected]/mongo/collection.go#L388: [if]: limit = int(deleteOne)
https://go-mod-viewer.appspot.com/go.etcd.io/raft/[email protected]/rawnode_test.go#L325: [if]: maybePlusOne = int(ok && autoLeave)
https://go-mod-viewer.appspot.com/go.flow.arcalot.io/[email protected]/schema/function.go#L322: [if]: expectedReturnVals = int(f.StaticOutputValue != nil || f.DynamicTypeHandler != nil)
https://go-mod-viewer.appspot.com/[email protected]/internal/compile/serial.go#L207: [return]: return int(b)
https://go-mod-viewer.appspot.com/gopkg.in/olebedev/[email protected]/api.go#L947: [if]: val = int(val)
And here are all occurrences in the standard library:
src/runtime/proc.go:5422:2: [if]: run0 = int(!iscgo && cgoHasExtraM && extraMLength.Load() > 0)
src/runtime/symtab.go:1062:2: [if]: mask = int(off == ^uint32(0))
src/reflect/abi.go:380:3: [if]: x = int(b.Get(i))
src/time/format.go:417:2: [if]: n = int(u == 0)
src/compress/flate/huffman_bit_writer.go:405:2: [if]: flag = int(isEof)
src/encoding/binary/binary.go:348:4: [if-else]: bs[0] = int(*v)
src/encoding/binary/binary.go:354:4: [if-else]: bs[0] = int(v)
src/encoding/binary/binary.go:361:5: [if-else]: bs[i] = int(x)
src/encoding/binary/binary.go:546:2: [if-else]: e.buf[e.offset] = int(x)
src/math/big/float.go:1377:2: [if]: sbit = int(len(r) > 0)
src/math/big/nat.go:235:3: [if-else]: c = int(cx < c2 || cy < c3)
src/crypto/x509/pkcs1.go:108:2: [if]: version = int(len(key.Primes) > 2)
src/internal/zstd/zstd.go:210:2: [if-else]: windowDescriptorSize = int(singleSegment)
src/go/printer/nodes.go:619:5: [if]: min = int(prev != nil && name == prev)
src/go/types/mono.go:198:3: [if]: weight = int(typ == targ)
src/internal/pkgbits/encoder.go:275:2: [if]: x = int(b)
src/compress/flate/deflate_test.go:180:3: [if-else]: b[i] = int(r.cur+int64(i) >= r.l-1<<16)
src/go/scanner/scanner_test.go:735:2: [if]: cnt = int(err != "")
src/go/types/hilbert_test.go:153:4: [if]: v = int(i == j)
src/strings/builder_test.go:105:3: [if]: wantAllocs = int(growLen == 0)
src/unicode/utf8/utf8_test.go:168:3: [if]: wantsize = int(wantsize >= len(b))