V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
Charlie17Li
V2EX  ›  Go 编程语言

[golang 语言] 为啥在 golang 中不支持将 bool 强转成 int?

  •  
  •   Charlie17Li · 15 天前 · 4607 次点击

    引言

    今天写代码,发现bool不能直接强转成int,这就导致如下代码编译错误

    type Status int
    const StatusSuccess Status = 1
    ...
    
    status := StatusSuccess
    succ += int(status == StatusSuccess)
    

    这种在其他语言,例如 c++看起来非常自然的强转为啥 golang 不支持呢?

    第二个问题

    于是开始 Google,搜到这篇博客,作者用了 7 中方法实现 bool 转 int ,这里摘抄几个。

    方法 1

    func boolToInt(b bool) int {
        if b {
            return 1
        }
        return 0
    }
    

    方法 2

    func Bool2int(b bool) int {
        // The compiler currently only optimizes this form.
        // See issue 6011.
        var i int
        if b {
            i = 1
        } else {
            i = 0
        }
        return i
    }
    

    方法 7

    func fastBoolConv(b bool) int {
        return int(*(*byte)(unsafe.Pointer(&b)))
    }
    

    性能对比

    way 1 6449ms
    way 2 2868ms
    way 3 6378ms
    way 6 7268ms
    way 7 2987ms
    

    尝试去看方法 2 为啥比方法 1 快,但是没有看懂,有大佬能解释下吗,issue 6011

    59 条回复    2024-11-14 11:47:35 +08:00
    shiyunjin
        1
    shiyunjin  
       15 天前   ❤️ 2
    https://0x0f.me/blog/golang-compiler-optimization/
    这篇文章不是解释了吗?
    编译器优化了
    Kisesy
        2
    Kisesy  
       15 天前
    我测了一下,go 1.23 方法 1 跟 2 好像都优化了
    Trim21
        3
    Trim21  
       15 天前
    Kisesy
        4
    Kisesy  
       15 天前
    @Trim21 我又看了一下,确实,刚才我用 VScode 看汇编显示不出来,现在显示出来了,方法 1 多了一个跳转
    不过,测基准,差不多
    wen20
        5
    wen20  
       15 天前   ❤️ 1
    统统 cast 库转换
    povsister
        6
    povsister  
       15 天前   ❤️ 4
    先说行不行
    int 和 bool ,在内存布局上就是完全不一样的,通常来说 bool 算上 padding 也不会有一个 int 那么大。
    那何来“强转”一说?

    再说是不是
    go 的出现,就是 google 的工程师为了解决 c/c++的某些问题。包括但不限于:隐式类型转换,void*,滥用模板元编程,晦涩的 memory model 等等。但是你想做的事情又把问题带了回来。
    c0t
        7
    c0t  
       15 天前
    只是因为标准没写吧,c 倒是有 "A prvalue of type bool can be converted to a prvalue of type int, with false becoming zero and true becoming one.",不过 reinterpret_cast<int>(bool) 应该也不允许?
    Kisesy
        8
    Kisesy  
       15 天前   ❤️ 1
    最简单和优化的写法还是得看 https://github.com/golang/go/issues/6011
    看来不能用 return 1 和 0 的方式,这样不优化,需要把 1 和 0 赋值给变量再返回才行
    c0t
        9
    c0t  
       15 天前   ❤️ 1
    @c0t 更具体的 https://en.cppreference.com/w/cpp/language/implicit_conversion#Integral_conversions ,也说了,这不是 cast/conversion ,而是 integer promotion
    grzhan
        10
    grzhan  
       15 天前   ❤️ 3
    我翻了下 Go 的 Github Issue ,其实有非常多人的提过这个 int(bool) proposal 。
    包括今年也有关于这个的讨论: https://github.com/golang/go/issues/6011

    看得出来不少 Go 团队的成员是支持加入这个特性的,主要反对的人是 rsc ,他认为这个变更工作量很大,同时觉得收益不高,Go 团队时间有限,要搞的话你们自己搞:1. spec 的变更; 2. 编译器和 go/types 的变更; 3. 把 Go 主仓库相关代码更新了以尽可能用上这个新特性,来证明这个特性对于 Go 而言是有用的。

    所以感觉更接近 Go 早期版本忽视了这个特性,然后现在随着 Go 发展要加进这个特性工作量大了就懒得搞了。
    Kisesy
        11
    Kisesy  
       15 天前
    @Trim21 老兄,帮忙再看一下,之前测基准没啥区别,感觉是不是直接内联优化了 https://godbolt.org/z/fh7WqMvhx
    mainjzb
        12
    mainjzb  
       15 天前
    千万别加入这种沙雕转换。
    写 C++的时候偶尔手误就会写成
    if( a=b) // or (a =1 )
    {
    // do
    }

    检查一小时才能在一个角落里看到错误
    sunfall
        13
    sunfall  
       15 天前   ❤️ 1
    @mainjzb 现在的编译器都会对这种错误警告的,当然,如果不关注警告另说。
    realpg
        14
    realpg  
       15 天前
    这么喜欢骚操作 还是回去 C 吧
    sir283
        15
    sir283  
       15 天前 via Android
    有没有可能,c/c++的 bool 就是 0 跟 1 ?不过是加了个 typedef ?你用 while(1){}也能实现 bool true 的功能。不要把你 JavaScript 的坏习惯带到 go 里面,更不要强行扯到 c/c++上。
    kandaakihito
        16
    kandaakihito  
       15 天前
    @sunfall #13 很多人写代码压根不看 IDE 的警告,一打开文件右侧全是飘黄警告
    bruce0
        17
    bruce0  
       15 天前
    @kandaakihito 确实是这样, 我让团队的同学都在 ide 里配置一下 golangci-lint 这个东西, 很多人不配置,还有的配置了不用,不看, 导致很多基础的,有问题的代码都提交上来了, 我发现了再去找他们, 现在 ide 和一些工具挺智能的, 很多常见的坑都能发现
    lisxour
        18
    lisxour  
       15 天前
    @sunfall 项目大起来,几十个 warning 那不都很正常,只要不飘红,很少关注 warning ,无意中多一个 warning 也很难注意到
    Sunzehui
        19
    Sunzehui  
       15 天前
    JavaScript 欢迎你,隐式转换多到飞起,爽歪歪
    ferock
        20
    ferock  
       15 天前 via iPhone
    好贴
    minami
        21
    minami  
       15 天前
    这编译器优化能力也太逆天了。。。难怪那么多人黑
    aloxaf
        22
    aloxaf  
       15 天前
    @mainjzb lz 说的是强转,你这是隐式类型转换
    whyso
        23
    whyso  
       15 天前
    因为你先入为主
    dyllen
        24
    dyllen  
       15 天前
    我搞这么多年,没印象在那里需要用到这种特性。
    sunny352787
        25
    sunny352787  
       15 天前   ❤️ 5
    能转才不正常吧? C/C++那种对 bool 进行++或者其他计算的操作逻辑意义是啥? bool 就是 bool ,真或假,凭什么假的加 1 就变真了? 0 凭什么就是假的?那我负数为啥又是真了?类型该是啥就是啥,别瞎用。以前写 C++的时候碰到有人给 bool 做数值运算我一定要喷到他以后再也不敢。逻辑就是逻辑,数值就是数值,混用你不出错就怪了。
    fffq
        26
    fffq  
       15 天前
    没必要
    fffq
        27
    fffq  
       15 天前
    语义也变得不明确了,得不偿失
    body007
        28
    body007  
       15 天前
    防御性编程必备操作,怎么到 Go 这里就行不通了。
    aloxaf
        29
    aloxaf  
       15 天前
    @povsister 内存布局不一样咋就不能强转了,go 不是照样允许 int32 转 float64 么。

    而且就一个 bool 到 int 的强转而已,和你下面说的东西都没啥关系,某些连 i32 -> i64 的隐式转换都不允许的语言照样也允许 bool 强转 i32 。
    Charlie17Li
        30
    Charlie17Li  
    OP
       15 天前 via iPhone
    @sir283 c++的 bool 实际细节大小和 int 不一样,而且 int 可以直接赋值给 bool
    FalconD
        31
    FalconD  
       15 天前 via Android
    我不到啊 建议写 if cond {1} else {0}, 感觉还不如 cast
    securityCoding
        32
    securityCoding  
       15 天前
    大哥,我求你们别搞这些花里胡哨的写法了好吗? 还嫌 c++不够脑残是吧
    grzhan
        33
    grzhan  
       15 天前
    发现自己贴错了 issue: https://github.com/golang/go/issues/64825 ( x
    这个讨论还提到一个点,就是常量,现在常量由于这个类型转换的限制可能对于同个常量会写两个类型( bool/int ),这在 Go 编译器和运行时的代码里就有出现( src/internal/goexperiment/exp_arenas_on.go ):

    const Arenas = true
    const ArenasInt = 1

    总之这事目前来看没有明确拒绝的理由,更接近于懒得搞,如果有人愿意费力气把这变更做了,感觉 Go 团队这边也会接受。

    (其实这种类似的情况在 Go 社区有很多,习惯就好)
    FalconD
        34
    FalconD  
       15 天前 via Android
    语言是否允许 cast bool → Integer 和 op 的用例不合理是两回事
    写 if succ then sc += 1 很好
    Ver. 1 和 Ver. 2 有差异只能说编译器乐色
    LcDraven
        35
    LcDraven  
       15 天前
    @mainjzb if(1 == a)这样写可以避免
    dyllen
        36
    dyllen  
       15 天前
    @Kisesy Benchmark 测试没区别呀
    aloxaf
        37
    aloxaf  
       15 天前   ❤️ 1
    @sunny352787 坏了,我搜了一下,linux 内核中少说也有几百处 bool 参与数值运算代码

    移位的: https://github.com/torvalds/linux/blob/master/sound/soc/codecs/wsa881x.c#L912
    按位或: https://github.com/torvalds/linux/blob/master/arch/arm/mach-omap2/display.c#L310
    相加的: https://github.com/torvalds/linux/blob/master/arch/arm64/kvm/arm.c#L308
    相减的: https://github.com/torvalds/linux/blob/master/arch/arm64/kvm/vgic/vgic.c#L262 (这里虽然用到了强制类型转换,但在某些人看来应该同样罪大恶极)
    相乘的: https://github.com/torvalds/linux/blob/master/net/sctp/sm_make_chunk.c#L3689
    …… 太多了,更本数不清
    aloxaf
        38
    aloxaf  
       15 天前   ❤️ 3
    本来就是个正常的需求,说有取舍也就算了,非要把它批倒批臭。
    泛型的事情过去才几年啊,忘啦?那个 proposal 还挂在 issues 上,天天地盯着你们啊!
    DOLLOR
        39
    DOLLOR  
       15 天前
    建议用 if (status == StatusSuccess) succ += 1 形式,语义上更清晰。
    qW7bo2FbzbC0
        40
    qW7bo2FbzbC0  
       15 天前
    go 编委会喜欢打脸,前期各种不方便之处被各种网红文举说成优点和取舍,泛型就是其中之一
    DefoliationM
        41
    DefoliationM  
       15 天前 via Android
    那你直接用 c++不就行了,用锤子 go
    bli22ard
        42
    bli22ard  
       15 天前
    int 互转 bool , 就是应该被阻止的,当你看到 if ddd 时候, 你就可以断定,ddd 是个 bool 值, 而不是还有可能是个 int 。这样不是更清晰吗?搞不到为什么要 bool 能强转 int 。c/c++代码漏洞多不是没有原因的
    adoal
        43
    adoal  
       15 天前   ❤️ 2
    搞内核和嵌入式的,会把 bool 类型拿来做各种骚操作,是因为 bool 在他们眼里是个 bit field 。

    而做应用开发的“新”语言圈子里,这十来年的趋势是恶补以前对 PLT 的不重视,很强调类型系统。从语义上讲,一个 bool 的值域跟数值类型没有任何必然关系,甚至 true 和 false 不一定是 1 和 0 ,也不一定是 other 和 0 ,我记得小时候用 LASER 310 上的 BASIC 时,条件比较运算的结果,假值是 0 ,真值是-1 ,很多 BASIC 都这样的,后来刚开始学 C 时还不适应。
    leonshaw
        44
    leonshaw  
       14 天前
    为啥 float64 和 rune 都能转 int
    sunny352787
        45
    sunny352787  
       14 天前
    @aloxaf 有就说明正确吗?动动脑子自己好好想想,存在就是合理的吗?
    sunny352787
        46
    sunny352787  
       14 天前
    @aloxaf 别拿泛型碰瓷,从夯土锤进化到打桩机不是你用锉刀修正误差的理由
    adoal
        47
    adoal  
       14 天前
    @sunny352787 你回我的语气让我感觉有点摸不着头脑……因为不熟悉 id ,甚至一开始以为你是拿 kernel 代码来支持 bool 和 int 互转的。我的意思明明是,不同场景的需求不同,拿来写业务的,主要矛盾跟写内核和嵌入式的不一样,对 bool 的需求不是从寄存器里直接抠 bit filed ,而是更需要保证不同类型值域的语义正确。说起来咱们才是一伙的,你用反问语气怼我做什么。
    adoal
        48
    adoal  
       14 天前
    @sunny352787 sorry ,看错了,嘿嘿……原来你怼的不是我。
    sunny352787
        49
    sunny352787  
       14 天前
    @adoal 汗...
    c0t
        50
    c0t  
       14 天前
    @adoal c 标准里就算你用 ub 的方式改了 bool 内存位置的值,再转换还是一样的,true 1, false 0 ,因为标准里规定了这个过程的结果,标准里规定了即正确,未规定则 ub ,这就是一切 c 代码的工作方式,也是 bug 的来源,ub 太多,所以产生太容易。当然,写现代 c++的人在需要保证 memory layout 的时候肯定用各类 *cast 函数。新式语言启用这种 bool -> int cast 的也不少,rust 一样能 bool as i32
    FalconD
        51
    FalconD  
       14 天前 via Android
    @bli22ard cast 和 conversion 都分不清就别来搞笑了
    ugpu
        52
    ugpu  
       14 天前
    @bli22ard 应用层你这么想没问题 你需要的是语义清晰
    C/CPP 本身就是底层应用多 . 对于很对硬件层的东西来说 都是 byte 字节...

    正轨 cpp 程序员都会用 static_cast. 这样显示的指定是安全的
    billccn
        53
    billccn  
       14 天前
    @povsister #6 楼主的烦恼和 int 和 bool 在内存的布局其实关系不大吧,需要用这个变量的时候都是要把它读进寄存器的,不管在内存里是多长,寄存器都是至少 int 那么长的。

    现在因为 golang 缺乏这个转换,还要专门写个条件判断,指令上至少一个比较+一个条件跳转+两次立即数载入,那个条件跳转还容易产生分支预测错误,整体属于掏肠子放屁。
    sampeng
        54
    sampeng  
       14 天前
    这种懒还是别偷吧。。0/1 和 false/ture 本来就是两个语意的事。编译器已经告诉你了类型不匹配。这种都属于两个语意也就是两个类型的事了。。必须要求显性的进行转换没啥毛病啊。。。
    allanpk716
        55
    allanpk716  
       14 天前 via iPhone
    用 go 是希望死板一点,免得有人写出神奇的绕来绕去的代码…比如最喜欢的就是强制格式化要求…
    FalconD
        56
    FalconD  
       14 天前 via Android
    @sampeng op 不是说 go 没有强转吗
    meiyoumingzi6
        57
    meiyoumingzi6  
       14 天前 via iPhone
    那么问题来了,在 bool 判断的时候 1 是 true 那么 2 呢,py 可都是认为 true
    bli22ard
        58
    bli22ard  
       14 天前
    @FalconD 如果你不搞笑的话,请列出,分不清 cast 和 conversion 的事实,别做个无脑的喷子。
    guzzhao
        59
    guzzhao  
       14 天前
    func Bool2int(b bool) int {
    i :=0
    if b {
    i = 1
    }
    return i
    }
    这样速度比较快吧
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5849 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 06:30 · PVG 14:30 · LAX 22:30 · JFK 01:30
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.