Hacker Newsnew | past | comments | ask | show | jobs | submit | tapirl's commentslogin

It looks the following code will be rewritten badly, but no ways to avoid it? If this is true, maybe the blog article should mention this.

    package main
    
    //go:fix inline
    func handle() {
        recover()
    }
    
    func foo() {
        handle()
    }
    
    func main() {
        defer foo()
        panic("bye")
    }


recover()'s semantics make it so that "pointless" use like this can be inlined in a way that changes its semantics, but "correct" use remains unchanged.

Yes, maybe some code uses recover() to check if its being called as a panic handler, and perhaps `go fix` should add a check for this ("error: function to be inlined calls recover()"), but this isn't a particularly common footgun.


> ... and perhaps `go fix` should add a check for this (

This is an impossible task. For a library function, you can't know whether or not the function is defer called.

Maybe this is not an important problem. But it would be better if the blog article mentions this.


'Not common' is comforting until you hit a codebase where recover gets abused and your 'safe' inlining breaks prod.


Great example, illustrating go1.26.1 go fix source inline transformation breaking program semantics. Raise it as a bug against go fix?


As I have mentioned, no ways to fix it. Because it is hard to know whether or not the handle function is called in a deferred call.


Thanks, that's a bug. We should never inline a function that directly calls recover. I've filed https://go.dev/issue/78193.


Or: your buggy code is no longer buggy.


You claim listens right for this specified example. :D

It is just a demo.


Another example (fixable):

    package main

    import "unsafe"

    //go:fix inline
    func foo[T any]() {
        var t T
        _ = 1 / unsafe.Sizeof(t)
    }

    func main() {
        foo[struct{}]()
    }
Go is a language full of details: https://go101.org/details-and-tips/101.html


another:

   package main

   type T = [8]byte
   var a T

   //go:fix inline
   func foo() T {
      return T{}
   }

   func main() {
      if foo() == a {
      }
   }
filed: https://github.com/golang/go/issues/78170 and https://github.com/golang/go/issues/78169


similar:

    package main

    //go:fix inline
    func foo[T [8]byte | [4]uint16]() {
        var v T
        var n byte = 1 << len(v) >> len(v)
        if n == 0 {
            println("T is [8]byte")
        } else {
            println("T is [4]uint16]")
        }
    }

    func main() {
        foo[[8]byte]()
    }



Go was my favorite language for a long time, and I have written many books and articles about it. However, since the release of Go 1.22 [1], that is no longer the case. Go 1.22 damaged Go's reputation for promoting explicitness and maintaining strong backward compatibility.

[1]: https://go101.org/blog/2024-03-01-for-loop-semantic-changes-...


for-loop variable capture was maybe the #1 worst decision in the language. It was never what you wanted. I appreciate Go's commitment to backwards-compatibility, but in this case breaking it was the right choice.


It is only right for for-each loops.

For 3-clause-for loops, if you have read https://go101.org/blog/2024-03-01-for-loop-semantic-changes-... carefully, it is hard to think it is right.


All the examples in that article are very exotic.

  for i, p := 0, (*int)(nil); p == nil; fmt.Println(p == &i) {
    p = &i
  }
Be honest, how many times have you actually seen code which depended on the address of a variable declared in a 3-clause for-loop remaining stable across loop iterations? Nobody does this. It's extremely weird and un-idiomaic. Heck, 3-clause for-loops are somewhat uncommon in and of themselves. Conversely, everyone who writes Go has experienced unintuitive capture issues with for-range loops.

Are you actually aware of any breakages this change has caused?


This one is just an example to demo one case of backward-capability breaking.

> All the examples in that article are very exotic.

Have you carefully read that article? All? You must be kidding. The article shows several cases used in practice.


this is a repeating pattern with languages; there's no commitment to design coherency, no governance, just a mad scamper to stuff features into the project until it becomes an absurd caricature of itself.

C++ did it so egregiously & disastrously you'd think language maintainers would have been scared straight. No, like moths to a flame, it is the preferred hill to die on.

This is how C99 keeps winning when it bleeping should not. It's settled science, however imperfect. It's not getting rearranged because someone read a blog post. It has stability in real-world clock-on-the-wall terms like nothing else.


The change made in Go 1.22 for 3-clause-for loops is not a new feature. It simply broke backward compatibility and old Go principles. It is much worse than C++'s stuff features.


What is your language of choice now then?


zig now.


This is one reason why I created TapirMD, which offers better specificity.


CSS sucks because it is sometimes elegantly simple and intuitive, other times a maze of quirky, frustrating hacks.


Similar to my "Go 101" books website, about 1000 line of Go code (started from 500 lines at 9 years ago). The whole website can be built into a single Go binary.


The details / summary feature can also be implemented with pure css without JavaScript. Here is an example: https://docs.go101.org/std/pkg/io.html, just click all "+" signs to expand contents.

We can also use pure css to implement tab panels. A demo: http://tmd.tapirgames.com/demos.html#section-demo-4

Modern css is powerful.


Note that pure HTML and CSS implementations of tabs using <details> and <summary> fail to meet several important accessibility criteria [1].

While you can make something that visually appears to act as a set of tabs, building it accessibly unfortunately still requires JavaScript.

[1] https://adrianroselli.com/2019/04/details-summary-are-not-in...


This is false, recently the details element has gotten support for grouping them: the [name] attribute. This effectively enforces tab-like semantics where only one of the grouped details elements can be open at a time.

This is a quite recent addition and the modern web is evolving too fast so I wouldn't put it past myself for missing this :)

Yay for progress and for JavaScript free solutions!


No, it's still true. I'm aware of that hack, but unfortunately it doesn't solve the problems with pure HTML and CSS tabs.

Crucially, the `name` attribute does not semantically turn a group of <details> elements into a set of tabs. All it does is introduce the (visual) behavior where opening one <details> element closes the others.

I posted a list of accessibility issues with the <details> hack in another comment: https://news.ycombinator.com/item?id=46415271


The pure-css effects I mentioned both don't use <detail>/<summary>.


Same caveat applies to the "checkbox hack" or any other pure CSS solution. You cannot create accessible versions of most complex controls like tabs without JavaScript.

(That first example could be created semantically and accessibly with <details> / <summary> though!)


what about, make it work in pure html and css, and enrich it with js to make it accessible?

rather than not working at all with js disabled


That's actually a common strategy called "progressive enhancement". The only thing is that your order is backwards: you should first make it accessible in pure HTML and CSS, and then use JavaScript to layer your fancy interactions on top.

So, for the tabs example, your baseline pure HTML and CSS solution might involve showing the all tab panels simultaneously, stacked vertically. Once the JavaScript loads, it would rearrange the DOM to hide all but one and add the tab-like behavior.


for "accessible", do you mean getting focused when pressing TAB key?


Here is a non-exhaustive list of issues you'll run into with various pure HTML and CSS implementations:

- Tabs should have an ARIA "tab" role [1], but <summary> doesn't accept roles [2].

- Focusing a tab must activate the corresponding tab panel [3], which requires JavaScript.

- Tabs should be navigable by arrow keys [4], which also requires JavaScript.

I want to be clear that I'm not trying to tear down your work. Your project looks cool and eliminating JavaScript is a noble goal. But unfortunately, as of today, it's still required to correctly build most complex controls on the web.

[1] https://developer.mozilla.org/en-US/docs/Web/Accessibility/A...

[2] https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/...

[3] https://w3c.github.io/aria/#tab

[4] https://www.w3.org/WAI/ARIA/apg/patterns/tabs/


> Tabs should be navigable by arrow keys [4], which also requires JavaScript.

It supports this now (with JavaScript). If not, try to refresh the page.


typo: s/with/without/


What is a good reference to learn modern CSS? I seems most books and online resources are quickly outdated.


I really don't know. I'm not a CSS expert. I've just picked up bits and pieces of CSS knowledge from Google and AI agents. These results often aren't perfect, so you'll need to make some adjustments.


SetFinalizer is deprecated by AddCleanup: https://pkg.go.dev/runtime#AddCleanup

AddCleanup might be too heavy, it is cheaper to just set a bit in the header/info zone of memory blocks.


> Rust can also do arena allocations, and there is an allocator concept in Rust, too.

Just a pure question: Is Rust allocator global? (Will all heap allocations use the same allocator?)


Rust, as a language, has no allocator.

The standard library provides a global allocator. The collections in the standard library currently use that allocator.

It also provides an unstable interface for allocators in general. That's of course useful someday, but also doesn't prevent people from using whatever allocators they want in the meantime. It just means that libraries that want to be generic over one cannot currently agree. The standard library collections also will use that once it becomes stable.


No. There is a global allocator which is used by default, but all the stdlib functions that allocate memory have a version which allows you to pass in a custom allocator. These functions are still "unstable" though, so they can currently only be used with development builds of the compiler.


So I designed TapirMD [1], a new markup language which is still readable but more powerful, to help me (a tech writer) create web content.

[1]: https://tmd.tapirgames.com


Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: