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 · 9 天前 · 4451 次点击

    引言

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

    再说是不是
    go 的出现,就是 google 的工程师为了解决 c/c++的某些问题。包括但不限于:隐式类型转换,void*,滥用模板元编程,晦涩的 memory model 等等。但是你想做的事情又把问题带了回来。
    c0t
        7
    c0t  
       9 天前
    只是因为标准没写吧,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  
       9 天前   ❤️ 1
    最简单和优化的写法还是得看 https://github.com/golang/go/issues/6011
    看来不能用 return 1 和 0 的方式,这样不优化,需要把 1 和 0 赋值给变量再返回才行
    c0t
        9
    c0t  
       9 天前   ❤️ 1
    @c0t 更具体的 https://en.cppreference.com/w/cpp/language/implicit_conversion#Integral_conversions ,也说了,这不是 cast/conversion ,而是 integer promotion
    grzhan
        10
    grzhan  
       9 天前   ❤️ 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  
       8 天前
    @Trim21 老兄,帮忙再看一下,之前测基准没啥区别,感觉是不是直接内联优化了 https://godbolt.org/z/fh7WqMvhx
    mainjzb
        12
    mainjzb  
       8 天前
    千万别加入这种沙雕转换。
    写 C++的时候偶尔手误就会写成
    if( a=b) // or (a =1 )
    {
    // do
    }

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

    而且就一个 bool 到 int 的强转而已,和你下面说的东西都没啥关系,某些连 i32 -> i64 的隐式转换都不允许的语言照样也允许 bool 强转 i32 。
    Charlie17Li
        30
    Charlie17Li  
    OP
       8 天前 via iPhone
    @sir283 c++的 bool 实际细节大小和 int 不一样,而且 int 可以直接赋值给 bool
    FalconD
        31
    FalconD  
       8 天前 via Android
    我不到啊 建议写 if cond {1} else {0}, 感觉还不如 cast
    securityCoding
        32
    securityCoding  
       8 天前
    大哥,我求你们别搞这些花里胡哨的写法了好吗? 还嫌 c++不够脑残是吧
    grzhan
        33
    grzhan  
       8 天前
    发现自己贴错了 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  
       8 天前 via Android
    语言是否允许 cast bool → Integer 和 op 的用例不合理是两回事
    写 if succ then sc += 1 很好
    Ver. 1 和 Ver. 2 有差异只能说编译器乐色
    LcDraven
        35
    LcDraven  
       8 天前
    @mainjzb if(1 == a)这样写可以避免
    dyllen
        36
    dyllen  
       8 天前
    @Kisesy Benchmark 测试没区别呀
    aloxaf
        37
    aloxaf  
       8 天前   ❤️ 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  
       8 天前   ❤️ 3
    本来就是个正常的需求,说有取舍也就算了,非要把它批倒批臭。
    泛型的事情过去才几年啊,忘啦?那个 proposal 还挂在 issues 上,天天地盯着你们啊!
    DOLLOR
        39
    DOLLOR  
       8 天前
    建议用 if (status == StatusSuccess) succ += 1 形式,语义上更清晰。
    qW7bo2FbzbC0
        40
    qW7bo2FbzbC0  
       8 天前
    go 编委会喜欢打脸,前期各种不方便之处被各种网红文举说成优点和取舍,泛型就是其中之一
    DefoliationM
        41
    DefoliationM  
       8 天前 via Android
    那你直接用 c++不就行了,用锤子 go
    bli22ard
        42
    bli22ard  
       8 天前
    int 互转 bool , 就是应该被阻止的,当你看到 if ddd 时候, 你就可以断定,ddd 是个 bool 值, 而不是还有可能是个 int 。这样不是更清晰吗?搞不到为什么要 bool 能强转 int 。c/c++代码漏洞多不是没有原因的
    adoal
        43
    adoal  
       8 天前   ❤️ 2
    搞内核和嵌入式的,会把 bool 类型拿来做各种骚操作,是因为 bool 在他们眼里是个 bit field 。

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

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

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