Hacker News new | past | comments | ask | show | jobs | submit login
Unreal Rust (maikklein.github.io)
399 points by ibobev on Sept 4, 2022 | hide | past | favorite | 122 comments



This is great, because Unreal is a C++ project that's not so easy to just Rewrite In Rust.

It's also nice it uses bevy's ECS. It's made to fit Rust's type system and borrow checking model, and is highly parallel. It's really awesome how easy it is to distribute work across cores with it, without risk of heisenbugs thanks to Rust's data-race-safety guarantees.

I know game development sees traditional software best practices as not applicable to them, but then games struggle with showstopping bugs and single-core performance bottlenecks. I hope someone will bet that Rust, despite being stricter and seemingly slower to iterate with, can be a net benefit from lower defect rates. Rust users outside of game dev praise the "if it compiles, it's usually correct", and that junior devs can be told not to write unsafe code, so they can't cause UB and memory corruption.


Why do specifically Rust programmers think that people in every domain are just misguided and waiting for someone to teach them the right development practices? The sheer hubris…

The kind of bugs that games mostly struggle with these days have nothing to do with the stuff Rust is targeting, like memory safety. They are “business logic” problems with many interacting systems resulting in unpredictable behavior, buggy level geometry, or poorly scripted missions not accounting for all player behavior. How exactly does Rust help with any of that?


Ex-gamedev here, I have no idea what you're on about.

Gamedev isn't some place of rainbows and unicorns that makes it a unique snowflake. I spent more than my fair share of time chasing down heap corruption, invalid iterators and all sorts of other fun issues.

If anything working on a console where the compile, cook, upload, play cycle can be long having something that removes a whole class of errors would have been great back then. I don't think anyone is saying Rust is a silver bullet but it certainly has aspects that are beneficial in certain contexts.


Oh I agree with you: memory bugs are annoying and I've had my share of them. My contention is that, while they waste time here and there, the biggest problems are 1 or 2 layers of abstraction up. I've probably spent 10x more time debugging random physics issues than I did dealing with invalid iterators or heap corruption.

But I mainly take issue with the cockiness of my parent post. Rust is a reasonable evolution which, as you point out, eliminates one class of bugs. But my parent post took it for granted that things should be rewritten in it whenever possible, and that the gamedev community is ignorant of best practices.

The tech industry's demographic pyramid has a really wide base, which is possibly why it seems to build cults around new programming languages so much. Last time it was Ruby, and if we all listened to the equally self-assured Ruby crowd, everything would now be message passing up the wazoo.


You should probably go reread their post cause I definitely didn't read the same thing you did.


Well… Many Rust enthusiasts treat it either as a silver bullet or everything else as unholy (unsafe).

Besides, syntax and complexity wise I think Rust is even worse than C++.

As a gamedev I am not interested in Rust. Zig, Jai and Odin seems much cooler/interesting.


> Well… Many Rust enthusiasts

I really hope this sort of strawmanning doesn’t become common here. In response to any reasonable discussion about Rust the response seems to be “you’re fine, but the other enthusiasts are so unreasonable”. Where are these enthusiasts? Why bring them up when you can just respond to the person you’re talking to?

It’s super annoying because even on irrelevant threads such haters will call people’s attention to these imagined enthusiasts. Like this comment from 6 days ago calling people jihadis if they like a language - https://news.ycombinator.com/item?id=32640154. Note that the jihadis didn’t exist on the thread, only in the commenter’s mind.


Looking at https://lwn.net/Articles/893346/, I'd definitely describe mjg59 as unreasonably bashing unsafe languages (like some Rust users), though he says at https://news.ycombinator.com/item?id=31251199 that he doesn't know Rust.


Yeah, the linked comment says it pretty clearly

> I will say that I've never written a line of Rust in my life and certainly can't be described as representing the Rust community in any way.

If this is the best example out there then it does feel like GP was arguing against a strawman.

But even otherwise, please let’s acknowledge that in any large community there’s going to be a minority who are loud and rude. It doesn’t advance the conversation much to point it out. They don’t represent the whole community any more than the comment I linked to represents the whole HN community.

All I can say is that whenever I’ve seen such rudeness it’s moderated out pretty quick, whether here or elsewhere.


The irony here is that you're also responding to comments by other people in different threads!


> Well… Many Rust enthusiasts treat it either as a silver bullet or everything else as unholy (unsafe).

I don't think anyone here is saying that, at least in this thread the parent(and myself) were both careful to qualify there might be contexts that it could be useful in. If you see anyone saying that Rust solves every single problem then feel free to call them out for being full of shit :).

I'm not going to be giving my designers a Rust tutorial the same way we did with scripting Lua, you're still going to have a FFI somewhere which will require unsafe and a bit more care. However having had maintained pretty significant codebases in quite a few languages I will say that Rust code is more stable, crashes less and still runs in the same footprints as C++(C gets the codesize edge by having a much smaller stdlib and surface area). It's about knowing where your tools do well and where they have rough edges and a better tool is appropriate.

Having to switch between C, C++ and Rust codebases on a regular basis I think you're underselling the complexity of C++ and overselling how difficult Rust is. C++ has a huge surface area, you have to figure out what subset you want to use(exceptions: y/n?, RTTI: ditto, stdlib or custom containers, etc). Then you get into Rule-of-5, move semantics, template metaprogramming, undefined behaviors(don't use global constructors/destructors). I could easily list 20 years of various ways I've seen C++ explode in spectacular ways(volatile semantics anyone?).

If you want to use C, Zig, or C++ more power to ya. No one is forcing you to use Rust but for those of us who are interested in a language that isn't dragged along with decades of baggage Rust offers a compelling development story and largely delivers on the areas it aims to hit(accessible systems development, memory safety and first-class tooling in my experience).


The complexity of C++ really does come from the yes/no decisions that every project, library, and code path get to take. Do you know if a library call will throw? hopefully they commented their code well! even better if they maintained comments for all of their dependencies...


Might a library call throw? Assume yes.

Next question.


And since about half of all game codebase are compiled with -fnoexcept... that means we are left assuming that all library calls may randomly crash or corrupt the stack


Trivial to fix.


If you don't know what's exploding left and right, how are you going to fix it?


Compile without -fno-except.

There is no substitute for engineering, if you care. So, engineer.


so what am I to do? wrap all libraries in a try/catch call? Run tests and hope I've exhaustively verified all possible states?


Catch exceptions at a few select spots where you actually know something meaningful to do about them. Keep cleanup code in destructors, where it always runs reliably.

Avoid triggering exceptions too hard to recover from. That might involve work, but less work than fielding error codes would have been. There is no free lunch.


Have you written any Rust code by chance? I personally think Rust is easier than C++, possibly because it is a lot smaller language than C++ (at present). It isn't magic, but it kinda feels like it. To be able to do low level coding knowing you can't blow your foot off is pretty cool.


I have, even if mostly hobby stuff.

Some stuff that places Rust going into the same kind of complexity as C++.

Figuring out how to turn DAG algorithms into tree based ones, two macro systems, async infrastructure, Pin and PhantomData special fields, GAT (when they arrive).

Lets see how it looks like with 40 years of production code, on the other hand C++ is indeed reaching PL/I levels of complexity for those that care about ISO legalese.


I guess it depends what kind of code you are writing. I've written all of the above w/o too much trouble, but yes, those are the harder parts. However, most of my code is just basic logic, so has very little interaction with that stuff (with exception of async, which I use every day). My C++ is more rusty (pun not intended), but I'm sure there are powerful features that you might use occasionally, but generally don't use much. My point is that day to day coding in Rust is very easy and pleasant, and every once in a while needing an advanced feature that you have to lookup doesn't necessarily make the common case challenging. Like any language, it is hard to tell all this without writing a large project in it.


I've been using C++ on and off since the 90s (borland C++ 5 times) Tried rust at initial release. Read a csv file line by line, split each line read (because I couldn't find any streams), converted to numbers and calculated some stats. Never again. What rust feels like: you are riding a bike, but it has six safety wheels instead of any normal wheels.


I think it’s fair to say that Rust has changed massively since initial release. There are certainly safety aspects, but doing what you describe should be trivial and trigger none or almost none of them.

The compiler’s error messages are fantastically helpful these days. Might be worth a tinker some rainy day, but understand being completely turned off of something as well.


One major change since initial release was the introduction of a more permissive borrow checker. The newer one accepts many more valid programs thanks to the introduction of non-lexical lifetimes.


You can always use “unwrap()” in throwaway code.

https://play.rust-lang.org/?version=stable&mode=debug&editio...

If that code ever gets more use than that you expect, you can trivially find all the uses of “unwrap()”, and add the proper error handling. And yes there are quite a few places where things can go wrong.

But this explicit “signing for technical debt” when it relates to error handling is a really convenient productivity feature in practice.

A simple throwaway CLI for your own use is absolutely fine to use unwrap(), and you can fix it later on if it becomes a problem.


Unwrap is good to use for things that are expected to work 100% of the time. If unwrap fails in such situations, it’s fine to have the program panic and shut down.


I'm no Rust expert, but I believe reading a file line by line, splitting them, converting to numbers and doing something with them is just a handful of lines using methods on iterators. I suppose it was much harder in the initial release.


Why on earth would you say complexity would be even worse?

I can't go along with this at all


> I don't think anyone is saying Rust is a silver bullet

Jeezus.


I don’t have a dog in this fight, but I’m also surprised to see proverbial fight spring up here so abruptly and a proverbial dog so rapidly entered into the fray. I didn’t see anything in GP suggesting anyone else is misguided, I saw them state clearly why they appreciate the technical choices of a project and hope others will too. To the extent they made any mention of preferences to the contrary, I read it as conciliatory.

As an outsider to both game dev and the general Rust ecosystem, but with a more than passing interest in the ECS data model (entirely apart from games!) and the prospect of using it safely with parallelism (entirely apart from memory safety concerns!), I found GP’s comment insightful and interesting. I find yours unnecessarily defensive.

I get that “in Rust” is a HN lightning rod, but maybe sometimes it’s okay for people to just appreciate someone’s personal project for the reasons they appreciate it, without it being an instant conflict?


This toy project already gives a nice example of something Rust delivered that the author found to be a huge pain in C++: "C++ has one big flaw in Unreal. You can easily crash the editor, either by triggering an assert or accessing a nullptr." but "if you ever unwrap an Option::None or do out of bounds access, unreal-rust will simply catch the panic, exit play mode and log the error to the console. It will never crash the editor."


Hah, I'd forgotten about that (I haven't touched Unreal in a couple of years). You're right, that would be a huge win.


Memory safety does matter for games. Especially in places like networked games, or anti cheat software.

Even in single player games, a game crashing and losing your progress sucks.

Rust’s non-borrow checker features help with other kinds of things. Rust isn’t only an ownership system.


For a single player turn-based game, I'm not so sure the tradeoff is as obvious as everyone says, after coding one in C++ and another in Rust.

It's turn based, so I don't need AAA game multi-threaded performance. It's single-player, so cheating doesn't really hurt anyone.

I use sanitizers to catch memory problems, but there have only been a few of those over the years. Maybe ECS just isn't that vulnerable to memory-safety problems?

The borrow checker prevents iterator invalidation problems, but most of my functions are read-only so there's not really a chance for those anyway, or many other single-threaded race conditions for that matter.

After this long using Rust, I'm noticing more and more that the borrow checker is imposing a lot of artificial complexity, when I compare it to my C++ version. We're often told that Rust is hard because the underlying problem is hard, but I've encountered many cases where that's not true, it really is just the borrow checker. And unfortunately, most of them are problems that Polonius won't fix.

Rust has a lot of great features (enums and cargo!) but I think one should think carefully about their domain and architecture before choosing a language, lest they pay too much cost for too little benefit.

Just my two cents!


> It's turn based, so I don't need AAA game multi-threaded performance. It's single-player, so cheating doesn't really hurt anyone.

I don't understand this, just because it's turn based it doesn't mean you can't squeeze more juice out of your CPU if you want to have all kinds of good graphics, fancy animations, fancy complex environments (lots of moving pieces, etc), physics simulation, etc.

Turn based is just one style of play but there's plenty of turn (or pseudo-turn) based games (like JRPGs) which struggle with platform limitations. One could even argue that games like Final Fantasy XIV are turn based.


> I use sanitizers to catch memory problems, but there have only been a few of those over the years. Maybe ECS just isn't that vulnerable to memory-safety problems?

To expand on this a bit. In games, objects generally fit into two categories:

1. Short-lived temporary objects, usually stack-allocated or arena-allocated, etc. e.g. a point vector, or matrix.

2. Long-lived objects with indeterminate lifetime. e.g. a character or item.

In the case of long-lived objects, there are various strategies you could use to make it safe e.g. using object IDs or custom smart pointers to refer to other objects that potentially might go missing instead of pointers which are bound to be corrupted.

Generally you want to register all of your long-lived objects in one central location that will handle memory management. Manual, reference counting or GC are all viable strategies depending on requirements.

I like Rust and would use it for games but mainly because of the package management.


> It's turn based, so I don't need AAA game multi-threaded performance.

Then you could just use a GC'd language. For example, you could use C# in Unity.


> We're often told that Rust is hard because the underlying problem is hard, but I've encountered many cases where that's not true, it really is just the borrow checker. And unfortunately, most of them are problems that Polonius won't fix.

Out of curiosity, what are your favorite examples?


I would be curious to know more about this case too, fwiw!

Personally, I've only struggled a bit with the borrow checker in a couples of cases: trying to have self/circular references when trying to model OOP, and when fiddling/interacting with some low level async code. Both were unnecessary in my case, and each situation was caused by a lack of understanding.


You can put recurring references in a type, you just need to Box it up first.

So you'd have something like:

   enum List<T> {
       Cons(T, Box<List<T>>),
       Nil,
   }
Since the list is now boxed up the borrow checker has a fixed sized object to put on the stack at compile time.


Previous post mentioned self/circular references. You can't do that with a `Box`. You can with `Rc<RefCell<List` or `*mut List`, but those options either have a runtime cost, or aren't borrow-checked.

Lack of self-referential types that are both safe and zero-cost is a legit limitation of Rust.


Hi! Thanks for taking the time to explain the concept! I'm familiar with these (and others) now, but I was curious to know what others issues the commenter alludes to


Fascinating!

Do you use async closures? That's the only case where I felt the borrow checker is a bit behind. Also I haven't checked in a few months and I know there was some work ongoing.

That forced me to start organising the code in a different way which is less functional but easier to understand, so it's not crystal clear what's happening.


In which cases they are already mostly using Erlang, Java, .NET languages, or Go on their server stacks.

Rust is a great C and C++ replacement for low level stuff, I am still not convinced it is worthy versus those languages for distributed computing in terms of the productivity automatic memory management brings to the table.


As mentioned, the type system helps reduce invalid states among many other things too!


People should probably stop saying that rust is about memory safety, its design has impact on a lot of others things (that I arguably, care much more about).

> many interacting systems resulting in unpredictable behavior[0], buggy level geometry[1], or poorly scripted missions not accounting for all player behavior[2].

[0]: Rust borrowing system (you know exactly who is allowed to touch each ressource at a given point in time) is great at pushing you away from architecture with too much mutable interaction. That is a side effect of the system rather than its design purpose but, for my point of view, it is one of the main draw to write large projects in rust rather than C++.

[1]: probably not something Rust will help you with.

[2]: Rust type system (and overall coding conventions) is really good at building things such that it will only compile if you check for all cases or, at the very least, leave some explicitly unchecked cases.


Sorry, I think "system" is an overloaded term. I'm not talking about "thread X changed my state", I'm talking about a scenario where the physics system wants to move the player collider, but you want to control its motion with hand-tuned code, so you set it to cinematic, which now causes everything in the scene to go flying when it touches it, because it has effectively infinite inertia. Or maybe you have a system that regenerates your health and an area where healing effects turn into damage and your health regen is now draining the health bar.

These types of interactions exist at a layer where Rust can't help you. Even outside of gamedev, I would claim that most software bugs we run into every day are in this category: too high-level for the borrow checker. (Button not connected to anything. Scrollbar hidden, but the page is too long. Microservices that upgrade independently without API stability.)

> [2]: Rust type system (and overall coding conventions) is really good at building things such that it will only compile if you check for all cases or, at the very least, leave some explicitly unchecked cases.

I invite you to try to teach Rust's type system to level designers.


I mean, dude, I’ve seen the C++ written by organizations with supposedly highly-competent programmers. I think maybe we should let programmers get a little help from language designers.


Sorry, that was a poor phrasing. I mean gamedev has distinct approaches/methodologies/culture/priorities from the rest of the software industry. If you move from gamedev to other application development, you'll find a lot of things taken for granted in one are not that common in the other (an example great GDC talk: https://www.youtube.com/watch?v=t9HRzE7_2Xc)

Other types of software projects also have their own complexity, budgets, changing requirements, and deadlines (admittedly not as major as a game release), but these things are managed differently.

Without telling all of gamedev that they're doing it all wrong, I'd like to say that perhaps there's another methodology that would also work. Especially that some games nowadays aren't just one-offs with a big-bang launch, but are maintained for years after initial release, which makes them look more like other software projects.


Why would you think that? All of the game developers I've met don't really view themselves as separate from traditional software best practices, just that there's a lot more involved in making a game than people are ever really aware of. They're also among some of the most talented programmers, the stuff they have to come up with is insanity sometimes, so it's certainly not about them not caring about best practices.

Games aren't struggling because they're written in Unreal's flavor of C++ (or whatever other currently existing engines and their respective languages), there are insane deadlines always being pushed. There is never enough time and never enough money. Neither of these problems are solved with Rust, at least not to a substantial degree.


> there are insane deadlines always being pushed.

Higher compilations times aren't all bad https://xkcd.com/303/


> Rust users outside of game dev praise the "if it compiles, it's usually correct",

Why do people keep echoing this sentiment? I don't find this to be particularly true, in my sense of "correct". Yes, it does what the code says it'll do, but rarely the problem I actually get stuck at are language/syntax/semantic errors, it's usually domain/logic errors.

Same can be said for PHP, JavaScript and a bunch of languages, as every program that is possible to run without error, is correct, at least in the eyes of the interpreter. But again, the difficult errors are not syntax/language errors.


I think the phrase is a misquote. I think the original quote is "if it compiles, it runs." Secifically for canonical, safe Rust. As in: "if it compiles it runs and does not explode in your face in a place you didn't expect."

After four years of Rust (and 25+ of C++) I find this very true for the former and rarely for the latter except in extremely simple programs.

And yes, this says nothing about correctness as far as what the code is intended to in the first place.

But you are wrong when saying this, too, applies to e.g. PHP or JS in this context. For the simple reason that they are dynamically typed alone. The class of errors/side effects that this enables is a whole different league.


>"but rarely the problem I actually get stuck at are language/syntax/semantic errors, it's usually domain/logic errors."

Exactly my case. I've forgotten last time I've had memory alloc / free/ overrun /free after use etc. related bug in my C++ projects. I am using modern C++ which in combination with STL containers makes me forget about all this C++ is scary FUD. I am however not being "turn everything into template" maniac and in general trying for code not to be fancy but rather readable.


I first heard this phrase applied to Haskell. Both Haskell and Rust have sophisticated type systems, Haskell more-so than Rust. With a sophisticated type system, you can accurately encode the rules of your domain in the types and thus, when the program compiles you can be confident it will do the right thing.

Having written a fair amount in both languages as well as many others I find this to hold true. The phrase isn't, primarily anyway, about Rust's memory safety guarantees in my mind.


Yeah, it's such as silly sentiment, which honestly makes me wonder about the skill of the programmers repeating it. Exactly none of the bugs I've fixed in the last month had anything to do with memory corruption or data races or other undefined behavior.


It feels like something was misquoted at some point.

The main benefit of Rust towards safety is not memory safety or data race safety or undefined behavior. The main benefit of Rust towards safety is a strong type system with affine typing. When used properly, this eliminates huge classes of bugs. Most of the bugs that I need to fight when I'm writing in Python or JS, for instance, are simply impossible in Rust.

Of course, when I'm writing C++ (which I don't do a lot these days), I really miss all the features that you mentioned.


I am always skeptical about these type of projects. Out of the box Unreal Engine supports C++ and Blueprint.

The problem with C++ is the slow compile times, and if you make mistakes the engine crashes. In general you produce stuff slower by using C++.

Blueprint is a visual scripting tool, it compiles fast. But it stores code as a binary blob in source control. And I'm personally not a fan of visual scripting, I would prefer scripting by just writing code.

So generally I prototype in Blueprint and then rewrite in C++ later on. I would love an intermediate language that has fast compile times and saves as a source file instead of a binary blob.

The problem is most of the projects that integrate with another language get abandoned after a while. MonoUE was abandoned, USharp was abandoned, now the newest attempt to integrate UE with C# UnrealCLR looks abandoned. SkookumScript doesn't support UE5. Haxe support for UE was abandoned.

Do I really want to risk writing a ton of code in a language that might not support future engine versions? To be honest, I don't want to take that risk.


This is great, because Unreal is a C++ project that's not so easy to just Rewrite In Rust.

True. Although rewriting Nanite in Rust would be useful. Nanite is a very clever data structure for meshes. It's brittle, and full of internal relative addresses within the data item, so it's very vulnerable to buffer overflows. If it becomes widely used outside of UE, it might become something that gets supported in GPU hardware.


> It's brittle, and full of internal relative addresses within the data item,

I am not sure how much you would gain by doing this.

Internal references are one of the things where it is very hard to express in safe Rust what you want to do (see doubly linked lists and graphs). You can get around that by using vectors and indexes, but that is bypassing the borrow checker, and with it, much of your safety and mutability checks.


I always want to be careful with phrases like "bypassing safety checks", because of course the Vec/index pattern is 100% safe and (like any 100% safe Rust program) will (approximately) never trigger undefined behavior. But it's true that what was formerly undefined behavior will still be a bug, where a stale index accidentally refers to a new object that happens to get allocated in the same spot. You can use slabs and generational indexes to catch these bugs, but I don't know whether the performance cost of generational indexes is acceptable in this case.


> here a stale index accidentally refers to a new object that happens to get allocated in the same spot.

Given we're talking video games, you could also not worry and let speedrunners take advantage of it... ;)


Yes, Rust is great for tree-like data structures. However, add one non-tree edge to your graph and you're in big trouble.

This demonstrates the flexibility of GC'd languages, where you don't paint yourself into a corner so easily.


Yes, Rust is great for tree-like data structures. However, add one non-tree edge to your graph and you're in big trouble.

Good summary. Most of the headaches I have in Rust come from needing something that's not a simple tree. Parts that are simple trees are really easy.

Nanite is a DAG, by the way. The whole point of Nanite is that instances are combined, recursively. A mile of chain link fence would be highly detailed sections of wire, combined into full wires, combined into fence panels... But not just all simple repeats. If there's a hole in the fence somewhere, that can be represented.


I really don’t get this perspective. People act like you need one self-referential struct and suddenly you’ve gotta switch languages.

Just use unsafe! That’s what it’s there for. You can use internally-unsafe semantics to expose an externally-safe API and nobody needs to care.

You get the Rust’s safety benefits everywhere else (not to mention all the other nice parts) and your unsafe code is limited only to the areas it’s truly necessary.


You can't always hide the problem behind an API, because for instance an API may call back into your code and access data. Or it may want to do that in the future. The "unsafe" world will spread like a virus.


Absolutely ridiculous.

Rust itself is written using plenty of unsafe code, all of which runs on top of fundamentally-unsafe CPU architectures. And yet it manages to expose a safe API to its users who somehow to write memory-safe Rust programs every day.

> You can't always hide the problem behind an API, because for instance an API may call back into your code and access data.

This is quite literally all that Cell and RefCell do and they hide this behavior behind a safe API.

Do memory-safety leaks in supposedly memory-safe abstractions crop up occasionally? Sure, nobody's perfect. But this idea that unsafety is uncontainable and spreads out everywhere until your whole program is infected and riddled with memory-safety bugs is completely detached from reality and regularly disproven by Rust developers on a daily basis.


Rust is still in a research phase. People are still figuring out how to do certain very basic stuff without a garbage collector. See e.g. how long it has been taking to get a decent GUI library with callback functions that can be closures that reference external parts of data.

In my view it is silly to do very high-level stuff in a systems language that was never intended for that purpose, and when you can clearly see the downsides of that approach; you are thinking of memory issues a large part of the time instead of being able to focus on your high-level functionality or even high-level security issues. Of course you can try to do it, but then it is research, not practical or necessarily sustainable programming.


> Rust is still in a research phase. People are still figuring out how to do certain very basic stuff without a garbage collector. See e.g. how long it has been taking to get a decent GUI library with callback functions that can be closures that reference external parts of data.

Are Golang, Ruby, Python et al still in the "research phase"? After all, none of them have native GUI frameworks written in them yet.

That said, you're moving the goalposts. The entire premise of my original reply was objecting to your claim that Rust paints you into a corner should you need a non-tree-like data structure. Which is absolutely ridiculous, there are countless examples of self-referential datastructures written in Rust. They use unsafe, which is what it's there for.


Original poster said of Nanite:

> It's brittle, and full of internal relative addresses within the data item, so it's very vulnerable to buffer overflows.

I think the hope was that rewriting it in Rust would make it more robust. But if the only way to write it efficiently is to unsafe your way to writing C++ in disguise, it's not clear it will be any more robust.


You're casually ignoring the entire rest of your program, which can now be written in safe Rust.

Just because some part of your program needs to use features unavailable in safe rust doesn't mean your entire program might as well be written in asm.


"Just use unsafe! That’s what it’s there for."

No, no. "Unsafe" should not be used because you can't figure out how to do some data structure in Rust. You'll get it wrong.

I have 27,000 lines of Rust in one complicated program with no "unsafe".


First, please consider the context of my comment.

Second, Rust itself is implemented with unsafe. Self-referential data structures are necessary for some problem domains, and those must be implemented with unsafe. Unsafe exists and is a tool that is sometimes necessary. When it is necessary, it should be used carefully.

Just because you haven't needed unsafe personally doesn't mean others don't. And the Rust developers aren't superintelligences capable of using it correctly while the rest of us mere mortals aren't.


>Nanite is a very clever data structure for meshes. It's brittle, and full of internal relative addresses within the data item, so it's very vulnerable to buffer overflows.

Using bounds checking is likely to kill performance on such data structures, while using `get_unchecked` or raw pointers will keep the issues.

But even then, I would love to see Nanite implemented in Rust.


It is a matter of how much the compiler is able to ellide.


Kinda late to the party, but just a heads up: in rust, bounds checks are only available on debug builds. I'm not familiar with the internals of Nanite though, so I can't comment on how safe (or unsafe) it should be to implement.


> in rust, bounds checks are only available on debug builds

This is not true - bounds checks are always enforced: https://play.rust-lang.org/?version=stable&mode=release&edit...

Maybe you're confusing it with signed overflow behaviour in Rust or bounds checks in Zig?


I’m not sure what you mean here, bounds checks have nothing to do with debug builds, other than that optimizations may remove more of them if they’re determined to never fire, and release builds tend to have higher optimization levels. Semantically they’re always on unless you use the get_unchecked method.


Neat!

I write a lot of C++ plugins that are used in both Unreal and Unity. I like to think of them as "side loading". They can do whatever they want, however they want. The API surface that interacts with either engine is pretty minimal.

I think "use Unreal as a renderer" is underrated. You could imagine writing your full simulation logic independent of the AActor framework and then using Unreal just for rendering. For a client/server game the server might not use Unreal at all!

I'm not sure how valuable it is to use Rust but also use Unreal's framework and blueprints. Bevy + Unreal is a pretty fascinating combo and how much lifting is done by each side can be done in lots of ways.


I'm waiting on Verse a new scripting language for UE by Epic called Verse. A Twitter thread posited it may be called Unreal Verse ;) It is supposedly inspired by Smalltalk and has some Lispy goodness. Can't wait. I played with Rust, and I think it is fine, but too much overhead for me to get productive with it. But that's just me. I click more with Lisp and terse languages (J, APL, Scheme, etc.). So many options to be grateful for!


> has some Lispy goodness

Some "lispy goodness" like what? Is it s-expressions? Or do you mean like it has things lisp invented: things like conditionals ("if/else" statements), garbage collection, recursion and first-class functions?

If the first, that'd be super interesting! If it's the latter, tons of programming language have implemented features like that before :)


Since most of those features are common now then I would guess its the ones that are not common.

From smalltalk I would like the message system so that all messages to an object only have one entry point. From lisp I would like the macro system and that it all looks like the same language and not the regexp-frankeinstein thing that Rust has.


I hope they don't go down the Rust path. A scripting language or DSL needs to be simple for game dev. They certainly are not going to rewrite UE5 from C++ to Rust!


It is so new, I couldn't tell you other than I had read it was going to be a mix of Smalltalk with some Lisp features. I think a talk of a REPL with hot reload while your game is running type of thing, and Simon Peyton Jones of Haskell fame is now a fellow at EPIC working on Verse among other things. Maybe it will be more Haskell-ish? Excited in any case to use something other than C++ and a hot reload REPL sounds great!


This reminds me of Unreal.js, which allows you to write games with Unreal Engine in JavaScript (V8, to be specific): https://github.com/ncsoft/Unreal.js.


Unreal really is a fun tool to use, and I highly recommend it over Unity for stuff like blueprints. Things like this are awesome as well.

What I really really want (and maybe I can have this easily!) is to be able to inline some C++/Rust inside a blueprint. Sometimes there are expressions that would be way easier to type out, or some tricky state logic that I can express with prose more nicely than with arrows.

I imagine the "serious" thing to do is to build out the C++ in some other file and then do the thing with my blueprint, but imagine if we had a completely smooth gradient for upping the tooling from blueprint to C++ when needed!


I haven't heard great things about it, but there is a Marketplace product that should let you do what you are after. Alternatively, you could just write the code you want in some BPFL file, and expose them as static functions.

https://www.unrealengine.com/marketplace/en-US/product/magic...


AFAIK you can call C++ code from a blueprint and other way around.


You can using these function macros to C++:

UFUNCTION(BlueprintCallable) // This is a purely C++ function that is exposed to Blueprints and can be called from blueprints

UFUNCTION(BlueprintNativeEvent) // This can be implemented in C++, Blueprint, or both (in the Blueprint implementation, call the Parent method and it will run the C++ function and then run the Blueprint function)

UFUNCTION(BlueprintImplementableEvent) // This is a purely Blueprint function (though it is defined in the C++ header) that can be called from C++.

I believe there may also be some funky reflection code you can create to invoke Blueprint functions from C++, but there's very little reason for that as BlueprintImplementableEvent does that in a cleaner way.

Source:

https://docs.unrealengine.com/4.26/en-US/ProgrammingAndScrip...


Yeah I’m talking more about the UX of doing it. I mean really I would love some ML-like language I could write some code snippets directly into the blueprint. Like anonymous functions but for blocks. Would be a disaster in a professional context but I would like it lol


Surprised to see nothing about the C++ interop work using cxx (and friends).

I looked into using this approach when I was playing around with the idea. Writing lots and lots of C wrappers seemed … wasteful.


Also excited about the Python dialect coming to Unreal, which should have a major effect on iteration time.


There is a github project that allows runtime Python scripting. I've tried it and it does work, however I tested it on UE4 and not UE5.

https://github.com/20tab/UnrealEnginePython


The trick here is "python dialect" rather than full-on python.

We need something that transpiles to native Unreal code, which has the majority of the verbosity and productivity aspects of Python.

Yes, Verse, if i'm not mistaken.

GDScript is another good example of this.


Could you elaborate on Python dialect?

Unreal already supports full Python for some stuff. They do also have a language called Verse in the works but I don’t believe that it has any relationship to Python


Last I checked, python support is editor-only.

I believe Verse is made by a small team who previously worked on a game-specific scripting language called Skookum script. I used it maybe a decade ago so my memory is hazy but I think you are correct, it didn't necessarily have much relation to Python. I remember it having some unique game-specific async constructs. I'm certainly looking forward to trying it out!



Oh wow I had no idea he had joined Epic. I'm looking forward to seeing what Verse ends up becoming.


You can use JavaScript to help with iteration time: https://github.com/ncsoft/Unreal.js.


These discussions always remind me of some talks by Jonathan blow where he argues rust doesn’t solve the hard problems he needed it to. There are podcast episodes with better discussions, but the main one people link to seems to be this

I haven’t used rust, but I sympathize with his problems with pointers and learned a lot from the various ways people try to solve them

https://m.youtube.com/watch?v=4t1K66dMhWk


He also admits he hasn't written more than a hundred lines of code with it before, and that was 4 years ago, more than half rusts lifetime ago.

My understanding is his priorities are C level control over memory, fast compiles, and ergonomics for the kinds of operations he commonly does.

Rust solves for memory/ergonomics, though the strictness it holds you to is in opposition to what (I understand) he considers ergonomic access & control of memory.

Therefore rust solves ~1 of his priorities, so it's not really a good fit. To extrapolate that to "doesn't solve the hard problems" is another logic step that I probably don't agree with.


Will it improve iteration time in Unreal?

Because C++ already is way too slow to compile wich hurts iteration time, wich is why most people fall back to plain simple blueprints, and even that can become quite heavy

Epic is working on a new scripting language that will improve this

What Rust brings to game development and how it's better than C/C++ in that regard?

Because you don't develop a game the same way, with the same constraints as a driver for example


No, it won't improve compile times, and that is a big drawback indeed.


Is it? With live coding how long do your compiles take?

TBH Unreal C++ is faster to compile than Unity C#. It is slower than an interpreted language. But I’ve also worked pipelines that chain Python executions which compound into ~5 second boot time.

Faster is always better of course. But I often think people overstate compile times. The upside is it means people work hard to improve things from good to great!


Unity is not a good benchmark, it's becoming slower every year

Hot reload is great for small edits, but anything that require substantial changes or refactors and you got to recompile everything, that's what's slow


How slow?

My Unreal C++ modules are pretty small. But on a P620 I’d say my time from editor -> compile -> relaunch editor in under a minute? I think I can compile and link the full engine in 15 minutes?

But I will admit my current Unreal projects are quite small.


As a c++ dev, I don't understand how anyone could ever tolerate a 1 minute compile-edit-run cycle. If it takes more than a couple seconds on my projects I start investigating build & launch times immediately; the productivity gained by fast iteration times is honestly exponential


That’s only if you change memory layout. If you only change code within a function then you can use LiveCoding and it’s maybe 7 seconds?

> the productivity gained by fast iteration times is honestly exponential

Preaching to the choir! Iteration speed is GodKing. But I’ve never seen a large game engine that had sub-second compile and startup times. C++ is a rather miserable language and just including any STL header can break that target.


You can build the engine within 15 minutes? That doesn't sound right, unless you've got 32/+ cores and a LOT of RAM. Dunno what a P620 is, so that may be the case.


I've never built Unreal. However on my own C++ projects - change something, compile and run takes couple of seconds in most cases. But yes - my development desktop is 16 core with 128GB RAM and my home server is 20 core with 512GB RAM. Extra RAM never hurts and it is not that expensive


It's not just the compile times that sting you in Unreal, but it's opening your project as well. With fast storage and a small startup level it's not terrible, but the close editor -> compile small change -> reboot project sequence still takes about 15 seconds for me. It's a bit off-putting, particularly after using Unity where you can recompile with the editor open. Hopefully Verse fixes some of those issues.


Lenovo P620, it’s the workstation every big company seems to be using these days. Yes, 32C/64T Threadripper with 128gb of RAM.


1 minute is crazy, is this is a small project? Last time I used unreal(years ago) it was similar, but I figured they would have improved that by now..


What use cases would be this suitable for? It seems this approach is a bit of a hack that would fall apart in use. It would be nice to program games using Unreal, using the Rust language, but this doesn't sound like that.

I respect that making FFI bindings to Rust would be very difficult or impractical, but that's what I think would be interesting.


This is really cool. It's projects like these that makes me want to experiment with random things. It might not make sense or feasible but that is what's fun about it :)


And I was thinking this was referring to the unreal mode on the 32 bit x86 processors which would have been very cool if it was possible with Rust.


same here - firs reaction in my head was this, and then I knew (milliseconds) later it'll be about the Unreal Engine... but "Unreal Mode" is stuck there first deep into the L0 cache line.


Modern processors have such massive caches that you could run MS-DOS entirely within a cache.


Get real, we can no longer protect you from the fact that unreal engine only runs in long mode!


It might be. I think Rust can run on 16 bit processors. In theory.


I was almost certain this would be about running Rust under DOS unreal mode, flat 32-bit address space in real mode.

https://en.wikipedia.org/wiki/Unreal_mode




Consider applying for YC's Spring batch! Applications are open till Feb 11.

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

Search: