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

If I remember Windows 95/98/XP correctly, these icons were in menus only if there was the same icon on the panel. This would let you see there's a shorter way to do the action.

Right now icons indeed just add clutter.

They also make you think how could the designer depict a concept.

For example, why should "Save" button look like a diskette. What if it was Jesus, like the Christ Redeemer statue. That actually could be a funny game, like in the post, to invent icons.


At first glance, it looks like Rust's channels with a polymorphic type -- when you receive from a channel, you do match and write branches for each variant of the type.

But I wonder if this can be a better abstraction than async. (And whether I can build something like this in existing Rust.)


As a foreigner I hadn't known Monty Python when I started learning the language and reading the docs, and I haven't noticed any of those. I guess they came across as just noise.

Law enforcement in Russia works differently than in the US, especially in politically charged fields. An exapmel: in the US, one man was charged of breaking construction codes because he was doing chemical experiments in the basement of his single-family house, in a block zoned accordingly.

I understand this is extreme, but a good illustration. He was doing something on his own, and was charged. Such enforcement is extremely unlikely in Russia even in todays situation. For instance, a recent law explicitly banned _searching_ for extremists materials, e.g. Navalny's party website (they're labelled as extremists ex-court by the Interior or the Justice ministry, I don't remember). But there's been just 1 court case since then. You can search whatever you want as long as you're not public about it. As soon as you get enough publicity, you do get on the radar.

Same kinds of examples: in the 1950's USSR some musicians were shadow-banned (there was no legal ban on them), and not published. A man made a lathe and carved disks with their music on used x-ray films. He was arrested when he got enough publicity and sold good deal of copies. He was charged not for copying them -- there was no ban on this -- but for illicit enterpreneurship, or speculation as it was called back then. Had he been doing this alone, he'd probably have not got under arrest.

I actually, think it's roughly the same as dealing with Torrent and trackers in the Western world nowadays.


Technically, the act of bypassing censorship by itself is still not even illegal. They did make it recently such that writing about VPNs is grounds for blocking wherever you've written about it.

https://www.cnews.ru/news/top/2024-11-22_v_rossii_zapretili_...


> Technically, the act of bypassing censorship by itself is still not even illegal

Seeking extremist materials is illegal as of September. If that is not "bypassing censorship" then what is?

By the way. Extremist materials is a big list of thousands of things that no one can always know. What it means for a normal person? If you use VPN you can be finding extremist materials, if you don't = then you don't (because they are all helpfully blocked)

After 3 months there is one guy with a case for looking up Azov for example https://meduza.io/amp/cards/mvd-vpervye-popytalos-oshtrafova...


Wikipedia says it's moved to Hungary in 2015.

There's a big difference -- when EU/US bans Russians from using Roblox and other things and seeing other culture, (or someone bans Russians or Iranians by IP), it's rightful and thoughtful decision to protect democracy. When Russia does the same, it's dictatorial censorship.

Not all governments are equal - though this cuts both ways.

Which bans are you referring to?

They’re referring to sanctions - persons/businesses residing in Russia, certain specific individuals and those working for specific Russian entities are locked out of much of the Western economy. I think it’s reasonable personally, but I can understand how a Russian 1000km from the Ukrainian frontline who used to sell jewelry on Etsy would be pissed.

The most affected were not those inside, but the emigrants. MasterCard and Visa blocked Russian banks, and the emigrees couldn't pay with their savings anymore. Some people got shadow banned by banks, their accounts closed, or money transfers rejected.

These people were on the Europe's side politically, yet they were targeted by just the passport.


None of the Russian expats I’ve met had this problem: after 2014 they all saw the writing on the wall and moved their money to western banks. I have sympathy for those that didn’t - normal people shouldn’t have to make this kind of calculus - but there’s no alternative to this while having useful sanctions. It’s not the causeless brutality of breaking someone’s window because of their accent.

Well, every expat I know, including me, had this problem and spent days working around. And the sanctions were very poorly designed, because the drones landing in Ukraine still have fresh American and German parts.

I'm not saying they should be lifted, but they punished the most exactly the pro-European Russians, inside our outside.


The way you phrase it, the banks were targeted, not the people or passports. Seems like anyone with money in a Russian bank would be in the same boat.

As someone from exUSSR, I'm shocked with the figures. We've given birth in Russia and in Kazakhstan, in both cases I paid nothing at all. Just taxi to the birth house (that's how the birth clinics are called), and my wife disliked the food, so I brought her some fruits and maybe corn flakes every day of 3 days she stayed there. The equipment I could see was modern, everything was clean, there was some confusion where to put her, otherwise everything was fine. If you're suspicious, you can go to private clinic at $1,5K..2K, they even send you a taxi (or who knows, maybe a private ambulance).

My libertarian friends criticise this from the point that "everything state-run is inefficient". Even if this is true, they fail to notice that state-run healthcare keeps prices of the private sector down. You don't have to sell a car if you're seriously sick.

In 1992-96 on Russian state TV, there was a translated program called something like "Rescue number 911", with nobody else but William Shattner. (We already knew him at the time.) I got an impression that the US healthcare was impeccable.


This was underwhelming. I work with Python and Pandas, and I can show examples of much clumsier workflows I run into. The most often, you get dataframe[(dataframe.column1 == something) & ~dataframe.column2.isna()] constucts, which show that python syntax falls short here, and isn't suitable for such manipulations. Unfortunately, there's no alternative, and I don't see R as much easier, there are plenty of ugly things as well there.

There's Julia -- it has serious drawbacks, like slow cold start if you launch a Julia script from the shell, which makes it unsuitable for CLI workflows.

Otherwise you have to switch to compiled languages, with their tradeoffs.


> Unfortunately, there's no alternative, and I don't see R as much easier, there are plenty of ugly things as well there.

Have you tried Polars? It really discourages the inefficient creation of intermediate boolean arrays such as in the code that you are showing.

> There's Julia -- it has serious drawbacks, like slow cold start if you launch a Julia script from the shell, which makes it unsuitable for CLI workflows.

Julia has gotten significantly better over time with regard to startup, especially with regard to plotting. There is definitely a preference for REPL or notebook based development to spread the costs of compilation over many executions. Compilation is increasingly modular with package based precompilation as well as ahead-of-time compilation modes. I do appreciate that typical compilation is an implicit step making the workflow much more similar to a scripting language than a traditionally compiled language.

I also do appreciate that traditional ahead-of-time static compilation to binary executable is also available now for deployment.

After a day of development in R or Python, I usually start regretting that I am not using Julia because I know yesterday's code could be executing much faster if I did. The question really becomes do I want to pay with time today or over the lifetime of the project.


Yes, I tried Polars both in Python and Rust. In Python it's more verbose, as the other commenter points out. In Rust it's very painful.


> Have you tried Polars? It really discourages the inefficient creation of intermediate boolean arrays such as in the code that you are showing.

The problem is not usually inefficiency, but syntactic noise. Polars does remove that in some cases, but in general gets even more verbose (apparently by design), which gets annoying fast when doing explorative data analysis.


To me, Go is like Rust oversimplified beyond reason. It edits your code when you don't ask, removing things you just started; it lacks iterators -- every time you must write a big cycle instead. It lacks simple things like check if a key exists in a map.

Proponents say it has nothing under the hood. I see under-the-hood-magic happen every time.

1) The arrays append is one example. Try removing an element from an array - you must rely on some magic and awkward syntax, and there's no clear explanation what actually happens under the hood (all docs just show you that a slice is a pointer to a piece of vector).

2) enums creation is just nonsense

3) To make matters worse, at work we have a linter that forbids merging a branch if you a) don't do if err != nil for every case b) have >20 for & if/else clauses. This makes you split functions in many pieces, turning your code into enterprise Java.

It feels like, to implement same things, Go is 2x slower than in Rust.

On the positive side,

* interfaces are simpler, without some stricter Rust's limitations; the only problem with them is that in the using code, you can't tell one from a struct

* it's really fast to pick up, I needed just couple of days to see examples and start coding stuff.

I think Go would have been great with

* proper enums (I'll be fine if they have no wrapped data)

* sensible arrays & slices, without any magic and awkward syntax

* iterators

* result unwrapping shorthands


One thing Go took from C that I dislike: overly short variable names (like in interface names when implementing function are usually 1 or 2 letters, but also chan!).

Other random things I hate:

- first element in a struct, if unnamed, acts like extending a struct;

- private/public fields of method based on capitalisation (it makes json mapping to a struct have so much boilerplate);

- default json lib being so inept with collections: an empty slice is serialised as null/absent (empty list is not absence of a list, WTF, but the new json lib promises to fix that json crap);

- error type being special, and not working well with chanels;

- lambda syntax is verbose;

- panics (especially the ones in libs);

- using internal proxy in companies for packages download is very fiddly, and sucks.

But, the tooling is pretty good and fast, I won’t lie. The language won’t win beauty contests for sure, but it mostly does the job. Still weak at building http servers (limited http server libs with good default headers, very limited openapi spec support).


> - first element in a struct, if unnamed, acts like extending a struct;

I’m not 100% sure what you’re referring to here. Struct embedding maybe? (FWIW struct embedding is not limited to the first field in a struct, hence my confusion)

> - error type being special, and not working well with chanels;

I don’t think the error type is special? Do you mean that it is the only interface implicitly defined in each package?

> - using internal proxy in companies for packages download is very fiddly, and sucks.

Yes this one is annoying. I ended up writing an entire go module proxy just so that it works with bearer tokens. It’s crazy that Go only supports http basic auth for proxy authentication in 2025.


> - error type being special, and not working well with chanels;

what?


I worked on a toy programming language (that compile down to golang), which is a fork of the go lexer/parser, but it changes how functions can only return one value allowing the use of Result[T]/Option[T] and error propagation operators `!` and `?`.

It has enums (sum type), tuple, built-in Set[T], and good Iterator methods. It has very nice type inferred lambda function (heavily inspired by the swift syntax)... lots of good stuff!

https://github.com/alaingilbert/agl


Go has iterators, had them for a while now. To delete an element from a slice you can use `slices.Delete`.

>3) To make matters worse, at work we have a linter that forbids merging a branch if you a) don't do if err != nil for every case b) have >20 for & if/else clauses. This makes you split functions in many pieces, turning your code into enterprise Java.

That is not a problem with Go.


> it lacks iterators -- every time you must write a big cycle instead

It has iterators - https://pkg.go.dev/iter.

> It lacks simple things like check if a key exists in a map.

What? `value, keyExists := myMap[someKey]`

> Try removing an element from an array - you must rely on some magic and awkward syntax, and there's no clear explanation what actually happens under the hood (all docs just show you that a slice is a pointer to a piece of vector).

First of all, if you're removing elements from the middle of an array, you're using the wrong data structure 99% of the time. If you're doing that in a loop, you're hitting degenerate performance.

Second, https://pkg.go.dev/slices#Delete


> `value, keyExists := myMap[someKey]`

If I don't need the value, I have to do awkward tricks with this construct. like `if _, key_exists := my_may[key]; key_exists { ... }`.

Also, you can do `value := myMap[someKey]`, and it will just return a value or nil.

Also, if the map has arrays as elements, it will magically create one, like Python's defaultdict.

This construct (assigning from map subscript) is pure magic, despite all the claims, that there's none in Golang.

...And also: I guess the idea was to make the language minimal and easy to learn, hence primitives have no methods on them. But... after all OOP in limited form is there in Golang, exactly like in Rust. And I don't see the point why custom structs do have methods, and it's easier to use, but basic ones don't, and you have to go import packages.

Not that it's wrong. But it's not easier at all, and learning curve just moves to another place.


> Also, if the map has arrays as elements, it will magically create one, like Python's defaultdict.

Err, no Go doesn't do that. No insertion happens unless you explicitly assign to the key.


You're right. But it will return something: https://go.dev/play/p/Cz6aeGpURgo

  my_map := make(map[int32][]int64)
  val := my_map[123]
  val = append(val, 456)
  my_map[123] = val
  fmt.Println(my_map)
prints `map[123:[456]]`

I guess it's convenient compared to Rust's strict approach with entry API. But also, what I found is that golang's subscript returns nil in one case: if the value type is a nested map.

  my_map := make(map[int32]map[int32]int64)
  val := my_map[123]
  val[456] = 789
  my_map[123] = val
  fmt.Println(my_map)
output:

  panic: assignment to entry in nil map


You can test for existence:

    val, ok := my_map[123]
    if ok {
        ...
    }
https://go.dev/blog/maps#working-with-maps


It returns the zero value for all types, including arrays (which is nil).

nil is equivalent to the empty array, which is why the rest of the code works as it does.


> Also, you can do `value := myMap[someKey]`, and it will just return a value or nil.

It might if your map is a `map[typeA]*typeB` but it definitely won't return a `nil` if your map is anything like `map[typeA]typeC` (where `typeC` is non-nillable; i.e. int, float, string, bool, rune, byte, time.Time, etc.) - you'll get a compile error: "mismatched types typeC and untyped nil".


_ is idiomatic and not an awkward trick. It keeps signatures consistent even when you don't need the value.


> But it's not easier at all, and learning curve just moves to another place.

Hard disagree. Go has its sharp corners, but they don’t even approach the complexity of the borrow checker of Rust alone, let alone all of the other complexity of the Rust ecosystem.


> First of all, if you're removing elements from the middle of an array, you're using the wrong data structure 99% of the time. If you're doing that in a loop, you're hitting degenerate performance.

Sorry but that’s not categorically true. Rather it’s highly scale-dependent. 90% of the slices in a typical code base will be 10s of elements long, in which case the memory overhead and mutation overhead are comparable to, say, a map. Oh and also it’s ordered and (in the above case) can fit within a cache line.


I used to do Go in production for several years, along with Java and TypeScript event-loop backends. It was a breeze of fresh air, especially for new projects, where the Java conventions could be put to rest. But in such an environment, with mostly Java legacy, people did tend to bend Go to the Java idioms rendering the PR reviews very cumbersome.

From what I’ve experienced, if you need any fine-grained control over your data or allocations, precision on the type level, expressing nontrivial algorithms, Go is just too clumsy.

The more I read about how people use Go today and what issues people still have, the more I’m happy I picked Rust for almost everything. I even find it much more productive to write scripts in Rust than in Python or shell script. You just get it right very quickly and you don’t need to care about the idiosyncrasies of ancient tech debt that would otherwise creep into your new projects. And of course the outcome is way more maintainable.

Not saying that Rust hadn’t had its own warts, but most of them are made explicit. This is perhaps what I appreciate the most.

Intuitively, however, I still notice myself creating a new Python or shell script file when I need something quick, but then something doesn’t really work well the moment the logic gets a bit more complex, and I need to backtrack and refactor. With Rust, this hasn’t been an issue in my experience.

And intuitively, I still tend to think in Java terms when designing. It’s funny how it sticks for so long. And when writing some Java, I miss Go’s use-site interfaces and TypeScript’s structural typing, while I miss nominal typing in TypeScript. It’s just maybe that you get used to workarounds and idiosyncrasies in some system and then carry them over to another paradigms.

I do like Go’s value propositions, and lots of its warts have been sorted out, but I’m just not as productive in it for my use cases as I am with Rust. It just checks way more boxes with me.


> This makes you split functions in many pieces, turning your code into enterprise Java.

Umm..in Java you won't have to split functions here. Maybe you should study some modern Java ?


I didn't mean that in Java you must split functions. I meant that code becomes a lot of functions with seemingly English names, but it's obscure what is happening.

p.s. upvoted, because some mob came and downvoted those who replied to me.


> proper enums

It has proper enums. Granted, it lacks an enum keyword, which seems to trip up many.

Perhaps what you are actually looking for is sum types? Given that you mentioned Rust, which weirdly[1] uses the enum keyword for sum types, this seems likely. Go does indeed lack that. Sum types are not enums, though.

> sensible arrays & slices, without any magic and awkward syntax

Its arrays and slices are exactly the same as how you would find it in C. So it is true that confuses many coming from languages that wrap them in incredible amounts of magic, but the issue you point to here is actually a lack of magic. Any improvements to help those who are accustomed to magic would require adding magic, not taking it away.

> iterators

Is there something about them that you find lacking? They don't seem really any different than iterators in other languages that I can see, although I'll grant you that the anonymous function pattern is a bit unconventional. It is fine, though.

> result unwrapping shorthands

Go wants to add this, and has been trying to for years, but nobody has explained how to do it sensibly. There are all kinds of surface solutions that get 50% of the way there, but nobody wants to tackle the other 50%. You can't force someone to roll up their sleeves, I guess.

[1] Rust uses enums to generate the sum type tag as an implementation detail, so its not quite as weird as it originally seems, but still rather strange that it would name it based on an effectively hidden implementation detail instead of naming it by what the user is actually trying to accomplish. Most likely it started with proper enums and then realized that sum types would be better instead and never thought to change the keyword to go along with that change.

But then again Swift did the same thing, so who knows? To be fair, its "enums" can degrade to proper enums in order to be compatible with Objective-C, so while not a very good reason, at least you can maybe find some kind of understanding in their thinking in that case. Rust, though...


> It has proper enums.

Well, then they look awkward and have give a feel like it's a syntax abuse.

> Its arrays and slices are exactly the same as how you would do it in C. So while it is true that trips up many coming from languages that wrap them in incredible amounts of magic, but the issue you point to here is actually a lack of magic.

In Rust, I see exactly what I work with -- a proper vector, material thing, or a slice, which is a view into a vector. Also, a slice in Rust is always contiguous, it starts from element a and finishes at element b. I can remove an arbitrary element from a middle of a vector, but slice is read-only, and I simply can't. I can push (append) only to a vector. I can insert in the middle of a vector -- and the doc warns me that it'll need to shift every element after it forward. There's just zero magic.

In Go instead, how do I insert an element in the middle of an array? I see suggestions like `myarray[:123] + []MyType{my_element} + myarray[123:]`. (Removing is like myarray[:123] + myarray[124:]`.)

What do I deal in this code with, and what do I get afterwards? Is this a sophisticated slice that keeps 3 views, 2 to myarray and 1 to the anonymous one?

The docs on the internet suggest that slices in go are exactly like in Rust, a contiguous sequence of array's elements. If so, in my example of inserting (as well as when deleting), there must be a lot happening under the hood.


Inserting elements in to a slice can be done quite easily since the introduction of the slices package to the standard library.

https://pkg.go.dev/slices#Insert


You shouldn’t need a library to do this simple operation.

I’m guessing the go language design went too far into “simplicity” at the expense of reasonableness.

For example, we can make a “simpler” language by not supporting multiplication, just use addition and write your own!


The operation is simple in concept, but can be costly from a compute standpoint when n is large. Multiplication has predicable performance. Insert does not. It being a function indicates that it is doing a lot of things and thus offers pause to make sure that the operation is acceptably within your operational bounds.

It could have been a builtin function, I suppose, but why not place it in the standard library? It's not a foundational operation. If you look at the implementation, you'll notice it simply rolls up several foundation operations into one function. That is exactly the kind of thing you'd expect to find in a standard library.


> Well, then they look awkward and have give a feel like it's a syntax abuse.

So nothing to worry about?

> how do I insert an element in the middle of an array?

Same as in C. If the array allocation is large enough, you can move the right hand side to the next memory location, and then replace the middle value.

Something like:

    replaceWith := 3
    replaceAt := 2
    array := [5]int{1, 2, 4, 5}
    size := 4
    for i := size; i > replaceAt; i-- {
        array[i] = array[i-1]
    }
    array[replaceAt] = replaceWith
    fmt.Println(array) // Output: [1 2 3 4 5]
If the array is not large enough, well, you are out of luck. Just like C, arrays must be allocated with a fixed size defined at compile time.

> The docs on the internet suggest that slices in go are exactly like in Rust, a contiguous sequence of array's elements.

They're exactly like how you'd implement a slice in C:

    struct slice {
        void *ptr;
        size_t len;
        size_t cap;
    };
The only thing Go really adds, aside from making slice a built-in type, that you wouldn't find in C is the [:] syntax.

Which isn't exactly the same as Rust. Technically, a Rust slice looks something like:

    struct slice {
        void *ptr;
        size_t len;
    };
There is some obvious overlap, of course. It still has to run on the same computer at the end of the day. But there is enough magic in Rust to hide the details that I think that you lose the nuance in that description. Go, on the other hand, picks up the exact same patterns one uses in C. So if you understand how you'd do it in C, you understand how you'd do it in Go.

Of course, that does mean operating a bit lower level than some developers are used to. Go favours making expensive operations obvious so that is a tradeoff it is willing to make, but regardless if it were to make it more familiar to developers coming from the land of magic it stands that it would require more magic, not less.


Ok, so mostly we agree. And I was right that you can't just concatenate different slices (e.g. to remove one item from the middle), hence Go has to do a lot of work under the hood to do that. I count this as magic.


> Ok, so mostly we agree.

I don't follow. Information isn't agreeable or disagreeable, it just is.

> And I was right that you can't just concatenate different slices

That's right. You would have to physically move the capacitors in your RAM around (while remaining powered!) in order to do that. Given the limits of our current understanding of science, that's impossible.

> hence Go has to do a lot of work under the hood to do that.

Do what? You can't actually do that. It cannot be done at the hardware level. There is nothing a programming language can do to enable it.

All a programming language can do is what we earlier demonstrated for arrays, or as slices allow dynamic allocation, if the original slice is not large enough you can also copy smaller slices into a new slice using a similar technique to the for loop above.

Go does offer a copy function and an append function that do the same kind of thing as the for loop above so you do not have to write the loop yourself every time. I guess that's what you think is magic? If you are suggesting that calling a function is magic, well, uh... You're not going to like this whole modern programming thing. Even Rust has functions, I'm afraid.

The Go standard library also provides a function for inserting into the middle of a slice, but, again, that's just a plain old boring function that adds some conditional logic around the use of the append and copy functions. It is really no different to how you'd write the code yourself. So, unless function are still deemed magic...


I may have misunderstood "you can't just concatenate different slices (e.g. to remove one item from the middle" but does [0] not do what you're talking about?

(with the caveat that anything else sharing `a` will be mangled, obvs.)

[0] https://go.dev/play/p/uQdoa3mUF00


I wish Go had sum types too. But I like being able to write a mutable tree structure without first having to read a whole book on the subject and inventing a new system of pointers. Every language is tradeoffs.


Yes, the language does not have ADT support, but there are projects that enable this, like https://github.com/alecthomas/go-check-sumtype.


As a C++ dev, such comments reinforce my hesitation to pick up either Go or Rust seriously :) It seems I already have the golden middle after all.


If only C++ had a fully supported cargo system


I like the language saying "it's not as easy as you think" when I'm about to do something ill-advised like roll my own mutable tree structure.


It is in fact not that hard when your memory is automatically managed


You understand that tree structures aren't just lookup tables, right? I wouldn't roll my own red-black tree (Jesus how tedious would that be). That's not what I'm talking about.


I mean, it is as easy as you think in a GC language


Pointers are orthogonal to sum types. They are completely different things and there is literally no tradeoff for the two things you describe. So you’re not making any sense.


No, you simply misread the comment.


upvoted to compensate the mob that came about and downvoted those who replied to me.


In one particular matter software engineering has measurements -- performance. Lots of cross-language comparisons, before-after. Although, it's still not done where it should be -- e.g. when planning an architecture, sometimes I personally hear "in the cloud it will be different" and nothing else.

In other areas, software engineering seems a lot like alchemy, ages before it became the serious science of chemistry.


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

Search: