There is inheritance in Rust. It even comes with the same variance confusion that you find in languages like C# and Java.
TehPers
- 0 Posts
- 47 Comments
TehPers@beehaw.orgto
Rust@programming.dev•Adventures in Rust borrowing deserialisationEnglish
2·14 days agoThis does seem like an issue with the library you’re using. Your second solution, using
RawValue, is likely what I would have gone with, bundled with a self-referential type (wrapping thePinin another nicer-looking type though). This is assuming I want to pass around a'statictype with the partially-deserialized data. In fact, I’ve done something like this in the past to pass around raw log data next to that same data in a parsed format (where the parsed data borrows from the raw logs).Alternatively, I’d have deferred the lifetime problem to someone else (library user for example) since the source data is probably provided at a higher level. This is how the libraries you’re using do it from what I can tell. They make the LT the user’s problem since the user will know what they want to do with the data.
TehPers@beehaw.orgto
Rust@programming.dev•Adventures in Rust borrowing deserialisationEnglish
51·14 days agoIf I had chosen to write this code in Go I never would have had to think about any of this and I expect it would have been fine.
Well the article is about zero-copy deserialization in Rust. If you just slap
#[derive(Deserialize)]on a bunch of'statictypes and let it copy the strings, then you don’t have this issue in Rust either.Also, you’d be using Go, not Rust. That’s fine, but not really relevant when you want to do JSON deserialization in Rust, is it?
What a wild conclusion to come to.
TehPers@beehaw.orgto
Rust@programming.dev•Is the async_trait still necessary for dync Trait with async methods?English
2·17 days agoBreaking down what
async fnin trait does, it converts it to afn method() -> impl Future<Output=T>, which breaks further down intofn method() -> Self::__MethodRetwhere the generated associated type implementsFuture.This doesn’t work for
dyn Traitbecause of the associated type (and the factmethod()needs a dyn-safeselfparameter).Instead, your methods need to return
dyn Futurein some capacity since that return type doesn’t rely on associated types. That’s whereasync_traitcomes in.Box<dyn Future<...>>is a dyn-safe boxed future, then it’s pinned becauseasyncusually generates a self-referential type, and you need to pin it anyway to.poll()the resulting future.Edit: also, being pedantic, associated types are fine in dyn traits, but you need to specify the type for it (like
dyn Blah<Foo=i32>. Even if you could name the return type from anasync fn, it’d be different for everyimplblock, so that’s not realistic here.
While I agree with your post, I do want to call out that Rust’s standard library does use a lot of unstable features and calls compiler intrinsics. Anyone can use the unstable features I believe with just
#![feature(...)], but not the intrinsics (not that there’s much reason to call the intrinsics directly anyway).
You can design a language where you don’t need to generate code to accomplish this.
Depending on what you mean by “generate code”, the only language at the level of C or C++ that I can think of that does this is Zig. Zig is weird though because you’re still doing what is functionally compile-time reflection, so in a way you’re still generating code, just in a different way.
If you’re comparing to Python, JS, or even C#, those all come with runtimes that can compile/interpret new code at runtime. None of those languages are comparable here. Rust, C, C++, Zig, etc compile into assembly, and type information, impl information, etc are all lost after compilation (ignoring symbol names or anything tracked as debug info).
If you’re specifically referring to Debug, Display, PartialEq, etc then the compiler doesn’t do that for you because Rust doesn’t assume that those traits are valid for everything.
Unlike Java where
new Integer(1) != new Integer(1)or JS where"" == 0, Rust requires you to specify when equality comparisons can be made, and requires you to write out the implementation (or use the derive for a simple, common implementation).Unlike C# where
record class Secret(String Value);will print out the secret into your logs when it inevitably gets logged, Rust requires you to specify when a type can be formatted into a string, and how it should be formatted.Just because a language does things one way doesn’t mean every language ever should do things that same way. If you want it to work like another language you like to use, use the language you like to use instead. Rust language designers made explicit decisions to not be the same as other languages because they wanted to solve problems they had with those languages. Those other languages are still usable though, and many solved the same problems in other ways (C#'s nullable reference types, Python’s type hints, TypeScript, C++'s concepts, etc).
Part of why Python can do this is that it runs completely differently from Rust. Python is interpreted and can run completely arbitrary code at runtime. It’s possible to
execarbitrary Python.Rust is compiled ahead of time. Once compiled, aside from inspecting how the output looks and what symbol names it uses, there’s nothing that ties the output to Rust. At runtime, there is nothing to compile new arbitrary code, and compiling at runtime would be slow anyway. There is no interpreter built into the application or required to run it either.
This is also why C, C++, and many other compiled languages can’t execute new arbitrary code at runtime.
Through macros? The term “meta-programming” had me lost since I’m only familiar with that in reference to C++ templates (and Rust’s generics are more like templates).
println!andformat!are macros because they use custom syntaxes and can reference local variables in a string literal provided to the macro:let a = 2; println!("{a:?} {b}", b=a);I don’t know how the derive macros would be function calls. They generate whole impls.
Macros generate new code. This is the same idea as C macros (except Rust macros generate syntax trees, not tokens, but that’s a minor difference).
So to answer your question as to why there are macros, it’s because you need to generate code based on the input. A function call can’t do that.
which has existed for much longer than has crates.io
The Rust compiler has not existed for as long as the debian package manager has. You’re still trusting it and its standard library even if your reason for trusting it is that Debian’s maintainers trust it. This is also true of any vetted dependencies you download. You’re trusting others at the end of the day, whether they are the package developers or the auditors. Only by auditing your dependencies yourself can you avoid trusting anyone else.
With that being said, you are also able to contribute here. Someone has to do the auditing. Go audit some packages!
The never type comes more from type theory and isn’t common in other languages (though TS has
never). Similar to 0 or the null set, it exists as a “base case” for types. For example, where you have unions ofT1 | T2 | ..., the “empty union” is the never type. Similarly, for set theory, a union of no sets is the null set, and in algebra, the summation of no numbers is 0.In practice, because it can’t be constructed, it can be used in unique ways. These properties happen to be super useful in niche places.
Because if it never returns, then it has no return value.
Then how would you annotate having no return value?
I don’t know where
Nonecomes from (what’s theTinOption<T>?)Assuming you meant
(), that’s a unit type with one valid value. It’s a ZST, but can be created and returned.!is a bottom type. It’s uninhabited. Can’t be created. Functions can never return it because they can never construct it. So why’s this useful? It can be coerced to any type.Because the set of valid values for
!is the null set, by contradiction, there do not exist any values valid for the type!that are invalid for any other typeT. Therefore, all valid values of!are also valid values of any other typeT, and you can always convert from it to any other type.Notably, this is already possible, but language support for it isn’t amazing:
enum A {} fn bar(a: A) { let foo: Box<Arc<Rc<Mutex<String>>>> = match a {} }Heck you can even do this today:
// `loop {}` never returns, so its type is `!`: let blah: String = loop {};
Saw Diesel mentioned, but also wanted to throw SeaORM out there. Might be worth looking into as well.
TehPers@beehaw.orgto
Rust@programming.dev•Switching to Rust's own mangling scheme on nightlyEnglish
3·3 months agoNo, the attribute is unsafe. An unsafe function is marked
unsafe fn.Also, unsafe by itself just means there are invariants that must me manually upheld to avoid unsound behavior. If those invariants are upheld, then it doesn’t matter if it’s unsafe.
TehPers@beehaw.orgto
Rust@programming.dev•Switching to Rust's own mangling scheme on nightlyEnglish
31·3 months ago#[unsafe(export_name = "my_super_duper_function")] pub fn foo() {}I would recommend reading up some materials on FFI in Rust if you’re interested. Calling functions in Rust from C and in C from Rust (or even languages other than C) is both extremely common and a primary usecase of the language.
TehPers@beehaw.orgto
Rust@programming.dev•Launching the 2025 State of Rust SurveyEnglish
2·3 months agoThs survey reminded me of a bunch of awesome features that I forgot were stabilized, many of which I can use in current projects lol. I know that wasn’t the intent, but it’s a nice side effect of filling it out I guess.
This is super cool! I love seeing these new implementations of JS. boa is another JS runtime written in Rust as well.
I’m curious how easy it is to embed this. Can I use it from another Rust project? Can I customize module loading behavior, or set limits on the runtime to limit CPU usage or memory usage or intercept network calls? Can I use it from a non-Rust project? Or is this intended to be a standalone JS runtime called from the CLI? I’ve been looking at Boa as a JS engine for one of my projects, but I’m open to checking out brimstone too if it’ll work.
TehPers@beehaw.orgto
Rust@programming.dev•Is it possible to cause undefined behaviour with static mut inside a function?English
1·3 months agoAnother commenter already explained why this is unsound, so I’ll skip that, though
static mutis almost universally unsound.Note, of course, that
main()won’t be called more than once, so if you can, I would honestly just make this a stack variable containing aBox<[u8; 0x400]>instead. Alternatively, aBox<[u8]>can make it simpler to pass around, and aVec<u8>that is pre-allocated withVec::with_capacitylets you track the current length as well with the buffer (if it’s going to have a variable length of actually useful data).If you want to make it a static for some reason, I’d recommend making it just
staticandthread_local, then wrapping it in some kind of cell. Making it thread local will mean you don’t need to lock to access it safely.
I already do #1, and I push for #3 (specifically Python or TS) where I can at work, but there’s this weird obsession with bash that people have at work despite all these scripts not running on Windows natively (outside WSL). Currently I do #2, but I often end up just stuck in bash the whole time because it’s needed for things as simple as building our code. I want to try out Fish as an alternative for those situations.
You can learn Rust whenever you want. There’s no rule that you must learn anything (including C) before learning Rust. Of course, knowing C will make the basic concepts, especially around memory management, a lot simpler.
If your goal is to eventually learn Rust, your next rabbit hole should be the book ideally. If you prefer a video format, I don’t have any specific suggestions since I don’t learn through videos usually, but I know there’s some good video resources on YouTube.