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

For the record, the mathematically correct answer to this question is that the year 2000 was the last year of the 19th century.

The reason is that year 0 never existed. The year 1 BCE was followed by the year 1 CE.

Culturally, anthropologically, and psychologically it might be a different matter. But 2000 years had not passed before the end of that year.


Is it Iceland? Ivory Coast? Ireland? Indonesia?

You're guesses are getting somewhat closer each time, almost there.

[flagged]


98,108 results on "India"

https://hn.algolia.com/?dateRange=all&page=0&prefix=false&qu...

174,402 results on "Israel"

https://hn.algolia.com/?dateRange=all&page=0&prefix=true&que...

I don't know how to tell they're "penalized" though.


It's completely false.

Pretty obviously the latter.

I don’t really see how declaring an unsafe block is materially different for the purposes of this discussion than, e.g., entering an async runtime.

It is code you need to write, tradeoffs you need to weigh, invariants you need to keep.


In Rust, if you have a function containing an unsafe block, you do not need to use another unsafe block to call the function. Therefore, unsafe is not “contagious” like JavaScript’s async.

> you do not need to use another unsafe block to call the function

And in C#, you can just type `await` and call an async function from a sync function.

Calling unsafe requires an unsafe block from safe functions. That's essentially the same thing as async/await in many languages (Rust does things differently, of course, but that's even worse in my opinion).


> And in C#, you can just type `await` and call an async function from a sync function.

Yes, but not in JavaScript.


Panics in Rust do not currently time-travel like that (including panics from failed bounds checks), and that's a good thing. The reason is that panicking does not imply terminating the process - they can be caught and handled, just like exceptions in C++. In fact, they use the same stack unwinding mechanism by default.

What the compiler is allowed to do is to shorten the loop by one and unconditionally panic after the loop, but this falls under the purview of the LLVM optimizer.


It's true that panics (unlike UB) cannot automatically time-travel, but your justification is weak. Recovering from panics can only prevent this optimization if the loop have side effects, and LLVM knows when panic=abort is set.

The post-panic situation is a problem in Rust. After a panic, you're in a somewhat abnormal state. Rust panics are not supposed to be a catchable exception system. If something other than program termination is in the near future, that's a problem.

That does create a problem for early panics, panicking when panic becomes inevitable but has not happened yet. This deserves more thought.


I mean, sure, dead code elimination applies to all optimized code. The important thing to understand is that panicking in Rust does not get magic treatment by the Rust compiler. It’s just a function that is declared in the type system to never return.

Once it shortens the loop, the compiler can also observe that `tab` is a local variable and therefore move the writes up "to the initializer." It can then see that the variable is unused and delete it, and also delete the loop.

[flagged]


A little slower but safe is a pretty good default I think. Most of the time you're not in a hot loop and even a 5% slowdown would be negligable.

And in the cases where you are in a hot loop you just have to put in a little extra effort to optimise it and gain the performance back, either by writing the code in a way that allows the compiler to prove correctness (e.g. using an iterator or assert), or by using the unsage keyword to "pinky-promise" to the compiler that your usage is correct.

IME that extra effort in performance-critical places almost always ends up being a lot less than the effort needed to avoid correctness/safety issues in mundane boilerplate/glue/plumbing code in C++.

Especially as Rust's package management system means that often you don't even have to do that optimisation work yourself: you can just pull in a crate that's done it for you (and Rust's safety guarantees make that a much less scary thing to do than it is in C++)


[flagged]


> Did you generate your comment using LLM?

(It was hand written. Typos and all.)


C++'s experience has caused Rust to rightfully learn the lesson that you don't allow optimizations to change the semantics of the program like that. Rust's goal is to be fast enough that any performance difference between C or C++ is too negligible to bother considering, and it's achieved that. It's not going to sacrifice reliability on the altar just to make up a measly 3% gap. There are plenty of ways that Rust's stricter semantics allow it to produce faster code than C++ (no move constructors or implicit copy constructors, thorough reference aliasing information, automatic generic struct layout optimizations, safe non-atomic refcounting, safe concurrent stack references, less defensive copying, etc.), it does not need to "convince C++ language people" of anything.

If you can detect a case where a panic "time traveling" would meaningfully improve performance, you could simply let the compiler issue an optional warning that would allow for auto-fixing the code to have those different semantics.

LTO cannot change the layout of structs. For something like a hash map implementation, it matters whether inner nodes store a pointer to the key and value, or whether it stores a pointer to each. To achieve this in C, you have no other options than emulating templates using macros.

The question is whether a hash-map implementation that works on a general `[key, index]` item and where index references at separate array of values isn't actually better for some access patterns ;)

And of course the other alternative to macros is code-generation (but macros are actually often fine).

But this also only matters for actually reusable generic code. If I'd implement a super-hot-path hashmap in C, I would stamp out a specialized version by hand instead of relying on a generic implementation. But for 90% of cases, a solution like in stb_ds.h is probably good enough.


Sure, but now you're actually moving the goal posts. We're talking about the practicalities - you can always achieve the same by doing more work, but it makes a difference that Rust gives you `HashMap` in the standard library that you can just use and get best-in-class performance, every time, with zero work, zero maintenance. The only choice you have to make is which hash function you want to plug into it, and since it is generic, that gets optimized and inlined as well (even with LTO disabled).

That's because the types of errors where you want a stack trace are a relatively small subset of all possible errors.

Stack traces are only useful for errors that indicate a bug in the program, i.e. something a programmers has to respond to. It's not useful for the vast class of bugs that are a result of wrong input, wrong external state, or infrastructure issues.

Rust projects tend to favor panicking over error handling for programmer bugs (which does indeed give you a stack trace depending on environment variables), or even better encoding the invariants in the type system, but there are cases where an error coming from a library are truly, actually unexpected, so both `anyhow` and `thiserror` do provide support for attaching a stack trace in those situations.


See? You get people explaining to you that you actually don't want a stack trace because xyz.

This sounds disingenuous. They explained why the language doesn't force stack traces on all errors, and then explained how to get them if you want them.

I see a very opinionated explanation, not a "this is why the language does not" explanation.

>Stack traces are only useful for errors that indicate a bug in the program, i.e. something a programmers has to respond to. It's not useful for the vast class of bugs that are a result of wrong input, wrong external state, or infrastructure issues.

This is a personal opinion, not something you can declare as the objective truth. There is a lot of value in seeing what path the program took before it encountered a eg. validation error.

>but there are cases where an error coming from a library are truly, actually unexpected, so both `anyhow` and `thiserror` do provide support for attaching a stack trace in those situations.

This is wrong because it's up to the library to attach the stacktrace, not the userland code using the library, so saying "you can get them if you want them" is not true. If the author of the library did not decide to attach the stacktrace, your only option is wrapping it yourself, which you can only do if you already know up front all the paths that can fail. Also, you are not supposed to expose errors from a library with anyhow, they are only for application/top level code.


I'm curious, what's the value of a stack trace of another person's library functions? As mentioned, you can get a stack trace that includes all of your code, that's what was offered to you.

The only thing a library gathering a stack trace instead of you gives you is that it includes traces through code you didn't write & ostensibly aren't responsible for. If you're going to go to the effort of tracing through a dependencies code, you might as well add the stack trace yourself; it's a single line of code from the standard library to collect it, std::backtrace::Backtrace::capture().

EDIT: capture will only actually grab a trace when env vars say it should, you can use force_capture to ignore those. To get to why this isn't the default for errors you're asking for, here's a line from their documentation:

> Capturing a backtrace can be both memory intensive and slow


Ideally (in my ideal world), it would be Result<T, E> that holds the backtrace. The value is that I don't know up front which method call is going to cause an error that is hard to track down, which is why I don't see how "instrument your calls with backtrace yourself" helps. It requires that I already have some idea about the execution path, otherwise I don't know where to put the backtrace instrumentation.

Since Backtrace::capture() is already tied to an env var, we could have the backtrace on Result without affecting performance, since you would only enable it for debugging. This would allow you to eg. easily track down a situation where you see in your prod logs that you are encountering a lot of "validation error: string is too long" but you can't tell where it is coming from. Flip the env var, redeploy the application, read the backtrace, turn off the env var, fix the problem.


> track down a situation where you see in your prod logs that you are encountering a lot of "validation error: string is too long" but you can't tell where it is coming from.

Capturing a stack trace is a hefty operation: making it happen on _every_ error creation, which would include creating an error in response to another error (like <failure to allocate> causing <failure to create object>) could easily grind a production server to a halt. Especially if there's correctly handled errors happening: every one of them will pay this cost, every time.

It sounds like a really specific problem here; the log line that's happening is generic enough that it doesn't identify which line of code is emitting the log, so you can't just add `capture` to that line (what logging system even does this? printf logging?).


I feel like we are talking past each other, because you ignored the whole part about "it is already tied to an env var, and it would be still tied to an env var" that you would only enable on demand, so who cares if it's a hefty operation? Also what about other languages that capture stacktraces all the time with exceptions, or scripting languages with type errors, where you can't even turn it off? Rust is somehow different?

It is a specific problem, so what? You see that you are sending 500 from an axum handler, and you are logging "serde deserialization error: line 4 invalid", wouldn't it be nice to see where that came from, without instrumenting all the places you are deserializing something?


Rust errors are not exceptions. Catching exceptions is unbelievably expensive in all languages that support them, compared to handling a Rust error value.

Some languages have exceptions as the only error handling mechanism (C#, Java, scripting languages), and it sounds like that's what you're used to. But this is also broadly agreed to be a severely limiting factor of those languages, resulting from being designed at a time when we didn't know better.

If you want to go fast (and Rust does), you cannot be catching exceptions in the hot path, and you certainly can't be throwing exceptions that carry stack traces, because walking the stack to build up the stack trace is many orders of magnitude slower than returning an error value.

Rust's error handling modes are designed with the benefit of hindsight from all those other languages from the last few decades, and reflects the fact that errors broadly fall in two categories: validation failures and programmer errors. The former should be a cheap error code that can be handled, the latter should terminate the program/thread/task and give you enough information to diagnose the problem.


I can't reconcile what you're asking for with the situation you're describing. If every single error everywhere in the program created a stack trace and logged it at creation time, your error would be lost under an avalanche of benign errors that are handled. And if you only want to selectively log _that_ error that's interesting, you need to selectively modify the place that logs it, which you don't want to do (because you don't want to have to find it).

It sounds like what you want is the errors you log to always log stack traces. Which is a fine position, I do something like that. It's just not something that can be the default, because it can't be done everywhere.


It will be literal decades before RISC-V becomes mainstream. Not because it’s not a perfectly fine ISA, but because business incentive structures aren’t nowhere near supporting it.

Literal man-millennia have been poured into writing software for both x86 and ARM, and nobody seems close to designing a competitive RISC-V chip.


String search algorithms would be one example, where a 64-bit register can be used as a “vector” containing 8x1 bytes.

Where is the part about unaligned pointers?

Strings typically consist of UTF-8 bytes, and any old `char*` pair has no alignment guarantees.

That's true, and that's why your typical string vector code has a prelude and a postlude to do the incomplete chunks at the ends. Between the ends, it's processing larger self-aligned chunks.

If you're aware of that technique, why were you asking about use cases for unaligned loads?

I'm saying that string search algorithms are _not_ a legitimate use case for unaligned loads.

You didn’t really say that, but feel free to share any reasons you might have to think so.

I don’t see any reason why it wouldn’t be perfectly fine on recent hardware, where unaligned loads are just as fast, and the cache pressure is identical for a linear search algorithm.


I asked where is the part about unaligned pointers in your string processing example. Saying that you want to load multiple bytes at a time does not imply at all that you have to do unaligned loads.

Doing unaligned loads using SSE or AVX might have been possible on Intel architectures for a long time, but it is still a little bit slower afaik. But anyway when you get into sub-architecture specific details like that, you've essentially left C-land, and you're essentially doing assembler level programming.


Every vectorized string search algorithm (including those treating an unsigned long as a "vector" of 8 bytes) currently needs a prelude that performs the search up to the first alignment boundary, and then performs the bulk of the search on well-aligned blocks, and then finally a postlude search in the tail of the string, where the tail is shorter than the block size/alignment.

Using unaligned loads, you can get rid of the prelude, including the associated branches and intptr arithmetic, and just have to deal with the tail.

If you're comparing short-ish strings, almost all of the time is spent in the prelude and postlude, even if the entire substring fits in a register. This is a silly language limitation when the hardware can actually easily just support the unaligned load.

In particular, it doesn't seem justified that what at most amounts to a tiny inefficiency in hardware turns into a very expensive class of bugs (UB).


Have you ever wanted to do this? I find the premise ridiculous.

But anyway, you're complaining that you have to work too hard to do unaligned loads (i.e. the wrong thing even if it should work on a particular machine) in C, when basically every other language makes you work more for basic systems programming tasks?

Whether unaligned loads can work on the machine level, it depends on the hardware. On some other architectures, you probably get anything from traps to unpredictable behaviour. It's totally fine that C does not define the behaviour for unaligned loads.

If you want to do some weird stuff like loading a single unaligned 16 byte quantity, where there was no "middle part" to begin with, just do memcpy then. The compiler might just do the appropriate thing on this architecture. Or if you need to closely control what's happened, write assembly then. But again, why would you even do this?


Who cares at this point. Nothing has ever managed to unite Europeans as effectively as the orange man’s unrelenting torrent of temper tantrums over the past year and a half.

Americans, we know some of you aren’t crazy. Can’t wait for the grown-ups to be in charge again, but in the mean time we’ll be moving on.


> Who cares at this point. Nothing has ever managed to unite Europeans as effectively as the orange man’s unrelenting torrent of temper tantrums over the past year and a half.

> Americans, we know some of you aren’t crazy. Can’t wait for the grown-ups to be in charge again, but in the mean time we’ll be moving on.

Assuming by "grown-ups" you mean Team Blue, then you'll be disappointed, because they manufactured consent for "orange man" every step of the way. People are too easily fooled by the good cop / bad cop routine, which is why it's continuously deployed.

We have a uniparty with red and blue facades whose illusion apparently even pervades overseas. Buckle in for disappointment no matter where you live. As if your country doesn't have similar power struggles.

It's capital interests against everybody else. Always has been. "Lesser of two evils" is still evil.


> Assuming by "grown-ups" you mean Team Blue

I am deeply, profoundly, emphatically uninterested in your "teams". Your local news are not relevant to us. What the rest of the world sees is US foreign policy, and the mainstream sentiment in every other Western nation right now is one of second-hand embarrassment.

Also the ethnic cleansing does not look great.


This is just more trite "Muh both sides" doomerism.

No it isn't.

It's safe to assume you've either drunk the kool-aid or have something to gain monetarily (even if falsely assumed) by allowing the illusion to persist. Either way, you're literally part of the problem, as is anyone else who still takes this system seriously.


> It's safe to assume you've either drunk the kool-aid or have something to gain monetarily (even if falsely assumed) by allowing the illusion to persist. Either way, you're literally part of the problem, as is anyone else who still takes this system seriously.

Is it? Am I? As a former Sanders caucuser who didn't fall for his supporters' both sides bullshit, perhaps some self-reflection on why running a candidate who's only popular with white people in Iowa would inevitably lose is in order.


Are you kidding? Hillary was hated by so many demographics. Forcing her (in a blatantly unfair fashion, to anybody paying attention) twice was so absurd that it qualifies as a prime example of manufacturing consent for capital interests.

"Vote for our capital shill or else you're hitler" is demonstrably not a winning strategy, especially when her Pied Piper shenanigans as revealed by wikileaks ultimately delivered us Trump.


Yeah. Couldn’t agree more. Trump and Obama are the same.

Nobody said same. In fact, on a surface level, they're easy to view as polar opposites. One must critically examine beneath the surface to see how they're both on the same team (capital).

What about sanders, mamdani, aoc? Or are they just fringe candidates and don’t count? For every AOC there are a dozen Schumers I guess. But I disagree with your thesis because there are factions of ethical capitalists in the democratic party that have never existed in the modern Republican Party.

As a former Sanders supporter, it became clear that even though Bernie was likely acting in good faith, the establishment used all of their levers politically and in capital-controlled media to limit him to the role of controlled opposition. Emphasis on "controlled."

> ethical capitalists

That is not a coherent concept, especially in late stage capitalism.


he's not actually a member of the party.

They let him run in their party primaries, rather than him being a democrat.

with that context its more sensible that the party brass wasnt particularly pro-bernie. they did their job by letting him run at all


Tired Reddit-tier take. This is why we are where we are now.

Thinking there’s no such thing as ethical capitalism is laughable. Your purity test is counter productive and ultimately just doomer signaling. Sure I get it billionaires are fuckkng us over but that has more to do with the failure of US democracy than capitalism.

That's why the "late stage" distinction is important. A case could be made for people who didn't know better decades ago. Hell, even Marx acknowledged it as a necessary bootstrapping phase of human societal development. But now, not so much.

I caucused for Sanders 2016 in Iowa. He lost the primary fair and square, and was mathematically eliminated before superdelegates even factored into the contest at all. Sanders had zero appeal outside of white, college-educated people like myself, whereas Clinton was very popular with minorities and white, college-educated people. She was just a better candidate for the Democrat primary, and if you want to win the general as a Dem, you have to win the primary first.

Blaming the media, capitalism, Debbie whatshername and insider elites is just Bernie Bro conspiracy theory making excuses for a bad, unpopular candidate.

> late stage capitalism

This is not a coherent concept, it's a term that doomers use to blame all of their bugbears with western society on shadowy cabals of nebulous elites. Ethical capitalism is, in fact, real – Elizabeth Warren, Robert Reich and Teddy Roosevelt are all examples.


Are you a DNC bot? You sure are hitting all of the classics.

Is reality a DNC bot? I'm someone who used to support Sanders, but I chose pragmatism and progress over digging my damn heels in and crying "conspiracy".

> crying "conspiracy"

You guys only have one line, don't you. That, and "buttery emails."

Not that Bernie winning would have fixed the underlying systemic problems, of course.

The grand spectacle created by systematic screwing over by capital interests was the biggest value of the Bernie campaigns. Basically, you can have somebody willing to stand up to monied interests, and they will get exactly as far as those monied interests allow, and not one inch further.

> but I chose pragmatism and progress

Delusional take, considering Dems delivered "nothing will fundamentally change" Biden followed by the most unpopular nominee in recent history, resulting in Trump Term 2. Controlled opposition by definition. "Oopsies, how did we ever lose this time?"

tl;dr: [my original comment]


> That, and "buttery emails."

I'm not familiar with the term lol

> Delusional take, considering Dems delivered "nothing will fundamentally change" Biden

Progress != fundamental change.

> followed by the most unpopular nominee in recent history, resulting in Trump Term 2. Controlled opposition by definition. "Oopsies, how did we ever lose this time?"

You're not talking about Kamala Harris here, right? The general candidate who lost the popular vote by less than 1.5%? That doesn't sound like the most unpopular nominee in recent history, surely you'd need a gap wider than that. Donald Trump himself lost the popular vote by much more than that (4.5%) to Joe Biden in 2020. Mitt Romney lost it by 3.9% in 2012, McCain lost it by 7.3% in 2008, and Kerry lost it by 2.4% in 2004. All wider margins than Harris.

How did you come up with the definition of "most unpopular nominee in recent history"? Are you basing that off of her performance in the 2020 primary – which she dropped out of before it even began? Are you constraining "recent history" to just mean the last four years? Do you mean "unpopular" as in "unpopular within the social media spheres that I frequent"?

You're not looking at the electoral college results and using it as a proxy for popularity, are you?


It should have been trivial to find a candidate that could beat Trump the second time around. Instead, they forced a weak pick, screwed everyone out of a proper primary, and acted confused when the vote was close. "Must have been the racist misogynists; time to double down on identity politics in the most self-unaware fashion."

It's so blatantly intentional, and rather convenient how it props up the illusion of a functioning democracy by having the vote be so close. But no, according to you, that's all tin foil. Cool, enjoy your shithole country you helped create.


Here's the thing: the grown-ups are in charge now. It's just that all spoiled, bratty children are too entitled to notice. It's like a scene from a movie where a serious teacher gets assigned to a class of misfits - no matter what the teacher does right, they'll always be "wrong".

Excuse me, what? I was writing both C and C++ 20 years ago, and UB was a huge part of the conversation (and the curriculum) back then as well.

There were a few high-profile "scandals" around GCC 3.2 (IIRC) because the compiler finally started much more aggressively using UB in optimizations, which was a reason that lots of people stayed on GCC 2.95 for a very long time. GCC 3.2 came out in 2002.


Started in 2005. Never ever did anyone complain about UB in my years of writing C code and patching other people's C code. I knew it exists - as a spec quirk. (Admittedly, never wrote a compiler and never used anything except gcc and clang.)

“More aggressively using UB” isn’t the right way to think about it.

In the C ecosystem, the compiler gets to define what UB means. They broke compatibility with their previous UB semantics, then blamed the language spec.


> In the C ecosystem, the compiler gets to define what UB means.

It really doesn't though. The current revision of the ISO/IEC 9899 standards document gets to define it, nobody else.


No; that says what things have undefined behavior in the language spec.

The compiler / os / hw platform are free to define the behavior of those things. Leaving them undefined at the language spec is the mechanism that allows them to be defined by the underlying system. C has worked this way since its initial release.


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

Search: