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

> I would say modern c++ written by someone already familiar with rust will probably be structured in a way that's extremely easy to port because you end up modeling the borrow checker in your brain.

I can't stress out how much important this sentence is. I would even remove the "familiar with rust" part.

Anyone who still thinks it's good to use C/CPP on modern hardware where Rust support is available and good: please print the sentence above and post it all over your place.


The reason folks still use c/c++ is because of ecosystem. Chances are you have a lot of code you depend on and don't have straightforward ways to port it (and all the transitive deps). Especially in the case of larger enterprise software where there are 10s of millions of lines keeping you entrenched. Foundational things like your threading model, async executor, etc can make it difficult. If you're operating in a micro service environment and your core does are both minimal and ported, the journey becomes much more tractable.

I assumed new projects but I just realised that I didn’t write it.

But still disagree: we have cbindgen.


cbindgen is woefully inadequate. Have you tried to use cbindgen to make interop between complex c++ work with rust? You will end up writing a significant amount of shim code manually, on both sides. Newer efforts like crubit seem promising, but still have some challenging edge cases.

Imagine you read a value from stdin and parse it as:

Maybe Int

So your program splits into two branches:

1. Nothing branch: you failed to obtain an Int.

There is no integer to use as an index, so you can’t even attempt a safe lookup into something like Vect n a.

2. Just i branch: you do have an Int called i.

But an Int is not automatically a valid index for Vect n a, because vectors are indexed by Fin n (a proof carrying “bounded natural”).

So inside the Just i branch, you refine further:

3. Try to turn the runtime integer i into a value of type Fin n.

There are two typical shapes of this step:

* Checked conversion returning Maybe (Fin n)

If the integer is in range, you get Just (fin : Fin n). Otherwise Nothing.

Checked conversion returning evidence (proof) that it’s in range

For example: produce k : Nat plus a proof like k < n (or LTE (S k) n), and then you can construct Fin n from that evidence.

(But it’s the same basically, you end up with a “Maybe LTE…”

Now if you also have a vector: xs : Vect n a

… the n in Fin n and the n in Vect n a are the same n (that’s what “unifies” means here: the types line up), so you can do: index fin xs : a

And crucially:

there is no branch in which you can call index without having constructed the Fin n first, so out-of-bounds access is unrepresentable (it’s not “checked later”, it’s “cannot be expressed”).

And within _that_ branch of the program, you have a proof of Fin n.

Said differently: you don’t get “compile-time knowledge of i”; you get a compile-time guarantee that whatever value you ended up with satisfies a predicate.

Concretely: you run a runtime check i < n. _ONCE_

If it fails, you’re in a branch where you do not have Fin n.

If it succeeds, you construct fin : Fin n at runtime (it’s a value, you can’t get around that), but its type encodes the invariant “in bounds”/check was done somewhere in this branch.


As much as I agree with you. Iran is signatory of the NPT with all its consequences.

Instead of letting more countries develop these weapons, we should work on denuclearizing all countries, starting with the US and Russia and their insane arsenals! And maybe build a unified international legal framework for civilian nuclear developments and applications from energy to medical outside of the "security council's" ferule!

A nuclear war cannot be won, thus never fought!


There's a very precise protocol when a signatory of the NPT is suspected of breaching it: first it has to go through the IAEA which has to be able to inspect whatever site, then it gets escalated to the UN, then a decision is taken, at the UN level on the matter.

Not unilaterally by Israel calling the world's superpower for help.

Your logic is as sound as "since my neighbor makes something illegal at home, I'm gonna shoot him and then call my buddy sheriff for help". It is obviously illegal.


Well I never advocated the latter, so please. And my logic is very sound, better than yours. :)

I was replying to "the US does not get to decide who can have a nuclear weapon and who does not". As much as I agree with that... my previous comment.

We're not talking about a nuclear program.


The next generation will include another processor to offload the inference from the RISC V processors used to offload inference from the host machine.


The next next generation will include memory to offload memory from the on chip memory to the memory on memory (also known as SRAM cache)


What core type theory is C3 actually built on?

The blog claims that @pool "solves memory lifetimes with scopes" yet it looks like a classic region/arena allocator that frees everything at the end of a lexical block… a technique that’s been around for decades.

Where do affine or linear guarantees come in?

From the examples I don’t see any restrictions on aliasing or on moving data between pools, so how are use‑after‑free bugs prevented once a pointer escapes its region?

And the line about having "solved memory management" for total functions::: bravo indeed…

Could you show a non‑trivial case where @pool eliminates a leak that an ordinary arena allocator wouldn’t?

Could you show a non‑trivial case, say, a multithreaded game loop where entities span multiple frames, or a high‑throughput server that streams chunked responses, where @pool prevents leaks that a plain arena allocator would not?


It is unfortunate that the title mentions borrow checking which doesn't actually have anything to do with the idea presented. "Forget RAII" would have made more sense.

This doesn't actually do any compile-time checks (it could, but it doesn't). It will do runtime checks on supported platforms by using page protection features eventually, but that's not really the goal.

The goal is actually extremely simple: make working with temporary data very easy, which is where most memory management messes happen in C.

The main difference between this and a typical arena allocator is the clearly scoped nature of it in the language. Temporary data that is local to the function is allocated in a new @pool scope. Temporary data that is returned to the caller is allocated in the parent @pool scope.

Personally I don't like the precise way this works too much because the decision of whether returned data is temporary or not should be the responsibility of the caller, not the callee. I'm guessing it is possible to set the temp allocator to point to the global allocator to work around this, but the callee will still be grabbing the parent "temp" scope which is just wrong to me.


> "Forget RAII" would have made more sense.

For memory only, which is one of the simplest kinds of resource. What about file descriptors? Graphics objects? Locks? RAII can keep track of all of those. (So does refcounting, too, but tracing GC usually not.)


You deal with those the same way you deal with them in any language without RAII, some sort of try-with-resource block or defer.

Not making a value judgment if that is better or worse than RAII, just pointing out that resources of different kinds don't have to be handled by the same mechanism. This blog post is about memory management in C3. Other resource management is already handled by defer.


Why would you treat the two differently, though? What benefit does it bring? (defer is such an ugly, manual solution in general; it becomes very cumbersome once you may want to give control of the resource to anyone else.)


Well, the answer is obvious in garbage collected languages: because a GC excels at managing memory but sucks at managing other resources.

Here, the answer is that ownership semantics are disliked by the language designer (doesn't fit the design goals), so they're not in the language.


Not really obvious, given that there are garbage collected languages with RAII, like D, to quote one example.

And even stuff like try/using/with can be made RAII alike, via static analysis, which defer like approaches usually can't, because it can be any expression type, unlike those other approaches that rely on specific interfaces/magic methods being present, thus can be tracked via the type system.

So it can be turned into a compiler error if such tagged type doesn't escape lexical scope without calling the respective try/using/with on the variable declaration.


Not disagreeing, just pointing out the reasoning.


Fair enough, also wanted to make a point of the possibilities, given that too many people place all GC languages on the same basket.


Just create dummy wrappers to make a type level distinction. A Height and a a Width can be two separate types even if they’re only floats basically.

Or another (dummy) example transfer(accountA, accountB). Make two types that wrap the same type but one being a TargetAccount and the other SourceAccount.

Use the type system to help you, don’t fight it.


Do you really want width and height or do you actually want dimensions or size? Same with transfer, maybe you wanted a transaction that gets executed. Worst case here use a builder with explicit function names.


I don’t really understand your point there.

Sound type systems are equivalent to proof systems.

You can use them to design data structures where their mere eventual existence guarantee the coherence and validity of your program’s state.

The basic example is “Fin n” that carries at compile time the proof that you made the necessary bounds checks at runtime or by construction that you never exceeded some bound.

Some languages allow you to build entire type level state machines! (eg. to represent these transactions and transitions)


My point is a Width type is usually not the sound type you are looking for. What probably wanted was a size type which is width and height. Or a dimensions type which is width and height. The problem was maybe not two arguments being confused but in reality a single thing with two elements…


Ah I see, it’s a solution too!


Another masterclass from the DODGY department.


The purpose of these institutions is to fund research, which is not necessarily profitable.

I'm pretty sure Rosenblatt was criticized back then for his non-tangible stupid ideas, and why the hell was his research was funded to begin with.


Particle on steroids.


No mention of the Cantor set (and its variants) indicator functions?


Well yes. The thing I particularly like about the Dirichlet function is it’s so simple to state and yet just completely breaks my intuition about so many things.


The cantor set has a simple statement. It's just the numbers in the unit interval without a 1 in their ternary representation.


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

Search: