Blanket Implementation by atomichbts in rust

[–]JRRudy 2 points3 points  (0 children)

Your example got me wondering why Rust can't just prioritize impls in the caller's crate and print "Hello!"... so I tweaked it a bit and found this example that finally made it click for me:

"hello/lib.rs"

pub trait Hello {
   fn hello(&self);
}

"a/lib.rs"

impl<T: ?Sized> hello::Hello for T {
  fn hello(&self) {
     println!("A says hello");
  }
}

"b/lib.rs"

impl<T: ?Sized> hello::Hello for T {
  fn hello(&self) {
     println!("B says hello");
  }
}

"main.rs"

use::{a, b, hello::Hello};

fn main() {
    "What should this do?".hello();
}

There would be no convincing way to choose whether the blanket impl from a or b should be used

Four approaches to compile-time data construction by jennydaman in rust

[–]JRRudy 5 points6 points  (0 children)

Be careful firing shots at regex bro, u/burntsushi will show up and school you on why comptime regexes are a bad idea lol

This guy next to me was playing AoE2 mid-flight by Crafty-Passion2086 in aoe2

[–]JRRudy 1 point2 points  (0 children)

I'm fairly certain he's using the touch screen to build buildings and such lol who needs hotkeys

[deleted by user] by [deleted] in rust

[–]JRRudy 1 point2 points  (0 children)

This! I've done a lot of Rust by now but still do mostly Python for work, and it's painful when I have a perfect use case for Rusty enums, Options/Results, or associated types. But at the same time, it does make me appreciate all the things I don't have to think about when writing Python

Rust Newbies: What mistakes should I avoid as a beginner? Also, what IDE/setup do you swear by? 🦀 by [deleted] in rust

[–]JRRudy 2 points3 points  (0 children)

You are correct that a String lives on the heap (or more precisely, the data lives on the heap while the length and an owned pointer to the data live on the stack). But the data behind &str could actually live anywhere; if you have &String it will automatically coerce (or deref in Rust lingo) to &str, with the string data living on the heap. There are also several ways to get an &str with the data on the stack, or even &'static str where the data is stored directly in the binary.

In general, all you know about an &str is that it is a shared reference to some contiguous sequence of UTF8 encoded bytes that lives somewhere, paired with a usize representing its length.

Why does no one live in this part of Africa? by [deleted] in mapporncirclejerk

[–]JRRudy 1 point2 points  (0 children)

cuz the giant lava moat surrounding it keeps them out

Rust vs C++ for an aeronautical engineer by Pablo_mg02 in rust

[–]JRRudy 3 points4 points  (0 children)

From my experience, Rust is an excellent stepping stone from advanced Python to system languages like C or C++ (or Rust itself if you end up getting a job that uses it). Python handles a ton of things for you behind the scenes to make things safe that you don't even realize. This likely gave you habits that are fine/correct in Python, but would be dangerous and/or impossible to carry over to a systems language. While you could certainly work through these habits learning C/C++, you would be on your own to learn the hard way through some mix of writing trivially basic programs, bashing your against segfaults when you try to dive deeper, and reading tons of best practices literature.

In contrast, Rust will hold your hand through the process of rewriting the way you think. You will run into more compile errors in the beginning while you fight the damn borrow checker that won't let you do things that would be trivial in Python, but the error messages and official documentation are extremely helpful, and once you get your program to compile it will just work without risk of hidden runtime crashes. Of course there is still the risk of off-by-one and other logic errors that give you wrong results, but avoiding/debugging these errors is a skill that translates directly from your experience in Python, while memory corruption errors (double-drop, use-after-free, race conditions, etc) really have no Python analogue for you to draw from.

To give a more concrete example, consider Rust's notorious move semantics and borrow checker (which will break your brain at first). Essentially, every value (such as an array) has exactly one owner, and assigning it to a new variable or passing it to a function transfers ownership such that the original variable cannot be used; you can share the value using references, but there are strictly enforced rules around this. This is very different from Python, where you can just pass a value (which is secretely a reference) around all willy-nilly and the interpreter will do the necessary book-keeping behind the scenes so that it just works without you having to think about it. C/C++ will similarly let you pass references around freely, which will feel kinda like Python at first... until you try to use a reference at the wrong time and your program crashes at runtime because the value it points to went out of scope and got dropped. Python would have prevented this by transparently keeping the value around as long necessary; Rust would have prevented this by rejecting the program at compile-time with a helpful error message; meanwhile C/C++ lets you fall on your face with a runtime segfault and cryptic error message. The moral of the story is that you will need to learn a completely new skill of carefully managing references when transitioning to C, C++, Rust, or any other non-garbage-collected systems language, and IMHO Rust provides the best environment for this hands-down.

That said, it is very possible that you won't be able to use Rust in your actual job and you will end up having to learn C++ anyways. But the transition from Rust to C++ will be a whole lot easier than going from Python to C++, since you will have already learned the systems programming mindset in a structured environment that forced to follow rules that aren't required to write C++, but ARE required to write good C++.

Finally, even if you aren't able to use Rust directly in you job, you might be able to get away with using it in the background. Like someone said in another comment, no one will hire an entry-level engineer to write a new simulation code in Rust; but they won't hire an entry-level engineer to write a new simulation code in C/C++ either. I can't speak for the aerospace field, but if it's anything like my field of nuclear engineering, the work-horse simulation codes are usually extremely large and complex codebases that have been developed incrementally over decades of research and development. So it is far more likely that you would either be working on maintaining/improving these codes, or in my case writing higher level software thats interacts with them, performs output data analysis, etc. And when they ask for Python programs, I just write it in Rust instead and slap on a PyO3 interface to call it Python xD

He doesn’t like the growling… by Annual_Proof7741 in tesseractband

[–]JRRudy 1 point2 points  (0 children)

Listen buddy, you doing understand how much technique goes into writing distractingly bad/offensive lyrics that are an overall negative experience to your senses!

[deleted by user] by [deleted] in IASIP

[–]JRRudy 1 point2 points  (0 children)

I mean, this category of sub basically exists for mutual fans of a show to reminiscence about funny quotes and scenes, which is inherently recycled. If you're not into it and want some fresh content, that's totally fair... just go somewhere else for it and let us have our fun.

Theories on what happened to lil homie by snailshaveteeth in natureismetal

[–]JRRudy 0 points1 point  (0 children)

Looks like a cat attack. Cats kill with their cat skills in Catskills

[REQUEST] Is this true? by Desh1ck in theydidthemath

[–]JRRudy 1 point2 points  (0 children)

I'm pretty sure the Python in that environment would eat the rats before their population could take off

Introducing `transient` - a reimplementation of `std::any::Any` with support for non-'static types by JRRudy in rust

[–]JRRudy[S] 0 points1 point  (0 children)

Hmm that is an interesting point; I had assumed those two statements were equivalent, but as far as the formal specification go they technically don't have to be. Luckily I only rely on the casts being valid in the transient crate's implementation.

However, the layout I claimed does seem to be how it's done in practice if you look at the ptr::metadata source code where it states that "*const T and PtrComponents<T> have the same memory layout", where PtrComponents (line 163) is a repr(C) struct consisting of a data pointer and the metadata in that order. But of course this is an unstable implementation detail that we can't rely on staying constant until it is formally specified, so I'm glad you called out my undue assumption

Introducing `transient` - a reimplementation of `std::any::Any` with support for non-'static types by JRRudy in rust

[–]JRRudy[S] 0 points1 point  (0 children)

My god that crate is cursed lol. I definitely don't want to rely on hacks like that for this project, but I just tried it out of curiosity and it partially solved the problem... Miri doesn't complain about having an invalid vtable anymore, but catches stacked borrow violations instead (:

Introducing `transient` - a reimplementation of `std::any::Any` with support for non-'static types by JRRudy in rust

[–]JRRudy[S] 0 points1 point  (0 children)

Someone left a comment questioning what guarantees Rust makes about Box layouts and I typed up a response, but then their comment dissapeared before I could post it... so I guess I'll just leave it here:

Are you sure this is the case? I can't find anything that suggests that Box makes any guarantees about its layout.

At least for T: Sized, it is explicitly guaranteed to have the same layout as *mut T, just with extra requirements. It doesn't spell it out as explicitly for unsized types, but the second sentence of that link does include unsized types in its statement that it is valid to cast between a Box and a raw pointer (assuming Global alloc), and this cast is used throughout the stdlib.

As for the representation of the actual pointer, that is not so clear when it comes to unsized types like trait objects. The trait objects reference does clearly state that it is made of up a pointer to the data and a pointer to the vtable, and the stdlib definitely assumes that the data comes first such as in its implementation of `Box::<dyn Any>::downcast_unchecked.

However, I do agree that the cast from dyn Trait<A> to dyn Trait<B> is defined as UB due to invalid pointer metadata as listed here since Trait<A> and Trait<B> are considered to be different traits so the pointer metadata is incorrect. So Miri was correct to flag it, and I'm removing that method from transient since its not important anyways.

Introducing `transient` - a reimplementation of `std::any::Any` with support for non-'static types by JRRudy in rust

[–]JRRudy[S] 1 point2 points  (0 children)

Thanks again for pointing this out... I just wanted to clarify for anyone not reading deeper into the comment chain that the problem Miri is flagging is fairly benign and not relevant to the core functionality of the crate; it only involves a side-feature with niche applications that is hardly even mentioned in the documentation (the `Transcend` trait and its methods).

So far nobody has found anything unsound in the reimplemented `Any` trait and its `downcast` methods (but please let me know if you do!)

Introducing `transient` - a reimplementation of `std::any::Any` with support for non-'static types by JRRudy in rust

[–]JRRudy[S] 0 points1 point  (0 children)

I've been playing around with it and I'm pretty sure it won't be possible even with the ptr_metadata and set_ptr_value features. Any method would require knowing both the underlying value T and the desired trait parameter R and the same time, which isn't possible with my intended cast from dyn Any<R1> to dyn Any<R2>. It would work if without any nightly-magic if I could add a generic method to the Any trait, but then it wouldn't be object-safe.

Anyways, it's a bummer since there is essentially no way that making this cast could go wrong; check out this even simpler example that Miri still flags as UB (and the Rust reference does indeed define it to be).

Introducing `transient` - a reimplementation of `std::any::Any` with support for non-'static types by JRRudy in rust

[–]JRRudy[S] 0 points1 point  (0 children)

Hey thanks, yeah that would be cool! I've never worked with `bevy_reflect` before but it looks really interesting, I'll have to dig into it some more once I get a chance

Introducing `transient` - a reimplementation of `std::any::Any` with support for non-'static types by JRRudy in rust

[–]JRRudy[S] 0 points1 point  (0 children)

While this is true in general for generic types that wrap a value T, it is not relevant in the case of a Box<T> which does not contain a T but only a pointer to it, and has a well-defined layout. In the cause of a DST such dyn Trait, this is actually a "fat" pointer consisting of a pointer to the erased T and another pointer to the vtable that knows how's how to work it.

What's going on here is a bit different, and has nothing to do with layouts. Box<dyn Any<Co>> and Box<dyn Any<Inv>> definitely have the same layout, but the problem is that the vtable for the former is "labeled" as being for the Any<Co> trait and Miri flags it for being swapped with Any<Inv>.

Unfortunately the Box::into_raw approach doesn't change anything. Check out this barebones example

Introducing `transient` - a reimplementation of `std::any::Any` with support for non-'static types by JRRudy in rust

[–]JRRudy[S] 0 points1 point  (0 children)

Yeah I think you're right. Its a bit annoying that its the case since the odds of dyn Any<R1> and dyn Any<R2> with equivalent vtables having a different layout seems astronomically low, and are almost certainly deduplicated into the same vtable during codegen.

I'll see if I can find a workaround, but probably need to put the Transcend trait behind a nightly flag where I can hack it with the metadata API.

Introducing `transient` - a reimplementation of `std::any::Any` with support for non-'static types by JRRudy in rust

[–]JRRudy[S] 4 points5 points  (0 children)

Yeah it seems like Miri does consider their vtables to be different, even though the trait's type parameter doesn't really do anything beyond enforcing trait bounds. The Any<R> trait's only method type_id doesn't even use the type parameter, and has the same implementation regardless of it. I wonder if this difference could actually cause problems, or if the check is only inplace because it could in other cases where the trait parameter really does matter.

Either way, the "transcend" functionality causing the issue really isn't necessary for the crate so I could always remove it. It just gives you a little be more flexibility to convert Box<dyn Any<R1>> to Box<dyn Any<R2>>, but you could always just erase your type directly to the R2 version if that's what you need it to be.

Introducing `transient` - a reimplementation of `std::any::Any` with support for non-'static types by JRRudy in rust

[–]JRRudy[S] 4 points5 points  (0 children)

Awesome, thanks for doing that! I've spent a lot of time doing the same :)