Forgot your password?
typodupeerror

Comment Re: A beautiful resurgence (Score 1) 53

Well, if you're not a Star Wars fan (I know Star Trek fans are Trekkies, what are Star Wars fans? Warries?), what're you doing on this post?

Well yes, I would be a trekkie. I don't know what starwars fans are, maybe jedists? But what got my interest in this story is the fact that Disney lost to not one but TWO indie films, despite how much they spent on the movie. Shit, they probably exceeded the budgets of both films combined on just marketing and promoting their movie.

Having said that, I don't believe "loud, annoying and stupid" is a valid reason to report somebody. If it was, I'd have done it already.

And the South Park guys had their own take on JarJar:

https://youtu.be/z-y4stp65HM?t...

Comment Re:I don't currently use Rust (Score 1) 161

Oooh...I think I heard of that before. At least, the concept is familiar, but I didn't know the name for it -- haven't needed to use one, though I don't typically deal in caching, aside from in-memory caching for high performance applications, but every time I've needed that, it needed to be lossless and highly concurrent and parallel, so this wouldn't work. For that, I've always relied on flurry.

An LRU map is similar but it is "primary storage" not cache, but still with a fixed capacity so when you try to put more in it than it can hold it has to discard something, and it does that by discarding the least-recently used item.

Ah, thus enabling stack allocation. Have you looked at this?

https://lib.rs/crates/small-co...

Has both a hash and a btree variant, both heapless, of lru. Might be worth building a struct that has the map contained in a refcell, then define your helper functions around the refcell to make your code more ergonomic. Let those helper functions handle the unwraps (or if you want to micro-optimize, then unwrap_unchecked, though usually this isn't necessary -- I've only found one case of a tight loop where the compiler couldn't optimize out the checks, which was for a custom lz77 implementation for unpacking/repacking data from a PS2 game.) As for passing your state around, there's a great, ergonomic way to do that already:

https://crates.io/crates/state

One other comment about something you said, which you might find interesting: You said that in Rust, moves are cheap. This isn't really true.

Depends on how you build your program. I think if you use that heapless version of Box, all you'd be moving is the pointer and nothing else. At least, that's the way Box works on the heap -- just an owned pointer, and when moved, the heap memory doesn't move anywhere, just the pointer on the stack does. And that is cheap.

Also if you're not doing it, I highly recommend using 'cargo clippy'. Despite the name, it's actually far more useful than it is annoying.

Comment Re:Stop beating a dead horse (Score 1) 53

They probably should have done with starwars what they did with battlestar galactica. I never saw the original, though from what I understand, the fans of the original were annoyed by some PC shit like making Starbuck a female, which I get because it's annoying when they really change a character you liked. But the new one was great regardless -- I liked Starbuck's character a lot, it, it would be weird to go back and watch the original with her as a man -- but it was a big success regardless of what the fans of the original thought of it. Sometimes, it's best to ignore what the fans think, because if it manages to be good and original, like what the BSG remake was, the fans will probably still like it anyway.

Comment Re:Rogue One, Andor, ... (Score 1) 53

Rogue One was actually good, a worthy companion of episodes IV and V.

That movie made barely any sense to me. All I really remember about it is the death star blew up a bunch of planets while the rebels smuggled out its secret vulnerability, and a CG Carrie Fisher at the end. I felt like I was watching Mortal Kombat 2 -- just a bunch of action scenes with very little story telling.

Comment Re: A beautiful resurgence (Score 1) 53

But, to just shove a character into the universe, then just vanish them, that's just bad practice.

George Lucas said he was supposed to be the comic relief character. Outside of that, the character is pointless. The problem is he wasn't funny, he was just an annoying CG version of drinkypoo.

Though I was never into starwars, so I don't know what appeals to starwars fans. The only thing that was interesting about the whole series was that it started Harrison Ford's career, and he's made a lot of very good movies since then. From what I understand, he cares as much about it as I do.

Comment Re:I don't currently use Rust (Score 1) 161

In that case, have you looked in the heapless crate? E.g:

https://docs.rs/heapless/lates...

You can "move" them by putting them in the heapless Box. Though you probably don't need to do that. I'm just not sure what your application/design goals are. Usually when I move something into a function and then move it back out, it's to use the builder pattern.

Comment Re:I don't currently use Rust (Score 1) 161

Moving something out of a hashmap and then back into it isn't cheap.

No, move the entire hashmap. That might sound like a big deal, but it's not: It's heap allocated, which means you always access it via a pointer. Moving just moves the pointer. That's the cheapest possible move you can get, especially given your CPU probably cached it.

In the my noalloc case

Not sure what you mean -- hashmaps are heap allocated.

In practice, it would be "copy the data to the stack, leaving the original in-place, then make sure to copy the mutated version back to the original location". The stack consumption of that approach would be worrisome.

The stack plane doesn't move anywhere. Unlike C++, rust's moves are always conceptually destructive. And it doesn't necessarily mean that all of the memory is copied either. If you move an entire hashmap, it's no longer usable within the original scope you were originally using it in, unless and until you move it back later. So conceptually, it's a move. But when it's compiled, that's a different story. Usually you're just copying a pointer, i.e. 8 bytes on 64-bit. The original pointer at its original location doesn't need to be deleted, the compiler simply doesn't provide any means for you to read from the original pointer's memory location, even though it's still a valid pointer until overwritten, which is allowed to happen after the move. This is different from a clone, which duplicates the entire hashmap, and THAT is expensive. There is no pointer aliasing in safe rust.

This is where the big-system biases of some of the Rust community work against it, and what fuels the certainty of C programmers that C is the right language for tiny devices. In C there would be no question -- you'd just look up the data in the map and return a pointer to it. Very efficient, no needless waste of several hundred bytes of precious stack space or any need to find some other memory region to copy it into.

You're still doing the exact same thing in rust, the only change is the semantics.

You say that "moves in Rust are cheap" but whether that's true is extremely context-dependent, and it's basically always the case that a pointer is even cheaper.

The context is pretty easy to understand though, and rust-analyzer makes it obvious even if you don't, namely by looking at its size. For example, if I stack allocate a very large array, which in rust is a slice, then a move would indeed be costly if the compiler determines that it's necessary to actually memcpy it first. But if in doubt, I can Box<T> it if I really need to move it, and then the move is guaranteed to be cheap.

Though most people don't even need to have that deep of an understanding of it, and can acquire that understanding over time (as I did) as it becomes necessary. When I started with rust, I didn't know what a heap or a stack was, other than how to overflow it with recursion. I had already been using rust for about a year before I even started calling myself a developer though, so go figure.

Unfortunately, the risks of the C approach are very well-understood, and my point in the post with which I entered this discussion was that Rust can be just as good for space and cycle-efficient low-level code as C while also providing significant security and safety benefits.

The thing with rust though, is it's very easy to do this, and even easier to iterate on it. The main problem with rust is that the way it does things is so different from other languages that seasoned developers tend to have a hard time with it, hence the learning curve. Think English as a second language vs just being a native English speaker. Everybody who isn't a native speaker thinks it's hard, where the native speakers think it's easy. Same thing with Mandarin. This also explains the struggle you're having here -- you're used to thinking one way, but the language asks you to think in another way.

This is why C++ developers often say that rust requires you to do a lot of planning before you can even begin prototyping, so it's too time-consuming, etc, even going as far as to make whole youtube videos about how much planning they have to go through to write rust, so the language is a waste of time.

Rust developers, like me, will straight up deny that. Why? Well, we find that we don't really have to plan anything. I think the best way I can describe it, in my particular case, is it's more about almost instinctively knowing what I shouldn't do more than it's about knowing what I should do, probably because rust makes what you shouldn't do a lot more obvious, even though it's also a "shouldn't do" in basically every language. That could come in the form of C and C++ being, when you declare a variable, it's mutable unless you say otherwise. In rust, it's immutable unless you say otherwise. And what ends up happening is that as those C and C++ developers become more experienced, they habitually start every variable declaration with 'const' just as I habitually always start it with 'let' rather than 'let mut'. Early mistakes snowball, so avoid doing the wrong thing early.

Speak of the time it takes to code, there's been a lot of research done on this, and at this point it's a foregone conclusion: Systems developers are more productive in rust. Like...a lot more. Google did a writeup on the reasons why this is the case a while back, where seasoned C++ developers will swear that this must be false because their attempt at rust some months ago taught them otherwise.

You see what I'm getting at?

There are plenty of people who understand the constraints of writing code on a device with 64K total RAM and a 1K stack who are writing Rust code and ensuring that the language is a good fit.

Oh like embedded? Actually I've done a fair bit of that, especially on nodemcu, fixed size stack allocator in tow. When you use no-std in rust, you've got all of that fine-grained control. Using the standard library is where you give up some of it, but only insofar as whatever it is that you don't understand about the particular standard library functions you're using. If in doubt, and you're using vscode+rust-analyzer, control-click the standard library function, and there's the source for it.

Comment Re:I don't currently use Rust (Score 1) 161

In your "works" function, you're evicting an already-evicted entry? What?

Yeah I'm not sure what "evict" really means, so it could have been just a "remove" helper function.

Also, you're still doing two map lookups, so you've (a) used my two-lookup solution

Not necessarily. The compiler *can* optimize this to just one, though it almost feels like you're trying to build an indexmap here. Even if it can't optimize this into just one, this can still be, and often is, more efficient code. Even if it was less efficient, that may be an acceptable tradeoff depending on the problem you're trying to solve. It is totally valid to sacrifice efficiency in favor of soundness, especially at a systems level. Whether you do or you don't really depends on the application.

(b) violated encapsulation by exposing the evict function and

Don't encapsulate data and functions this closely. In fact, encapsulation is often overused/overrated, and this is a case of that. I wouldn't dismiss it as it can be demonstrably useful, but it only is to a point. Even C++ developers have started realizing this and moving in this direction, precisely because encapsulating this way introduces bugs and makes your code less performant. Case in point:

https://www.youtube.com/watch?...

Notice in one of his slides are the words "some read only duplication improves performance and readability." That's a complete and total win, and he actually provided a QED somewhere in that presentation. I tend to prefer to avoid going too far into academics of computer science because it often misses the forest for the trees, though I do tend to think object oriented, while sounding good at the time it was conceived, is crap. The PhDs might disagree, but they're not the ones getting their hands dirty.

Regardless, you're trying to do object orientation in a language that is not object-oriented. If you still want to stick to object oriented, then you might look at it this way: Your recently_evicted function is both a getter AND a setter at the same time. Even in object oriented, you just don't do that.

(c) returned a weird error to the caller...

The error doesn't matter, the purpose was to make it the same type. Speak of same types, in your get_mut function:

Some(self.map.get_mut(&id)?)

Is really just:

self.map.get_mut(&id)

I should probably get more comfortable with RefCell; for some reason the concept rubs me the wrong way

I wouldn't. Just use it as an escape hatch when there really isn't any better option. If I find myself heading into that trap, the first thing I think is: "There's probably a better way" and 99% of the time, there is.

As for the comments about not returning &mut, the other option is to remove and re-add the entry on every usage, which is far worse than searching the map twice. `get_mut()` is not evil. It exists for very good reasons.

Of course it does, just don't misuse it. Get_mut is when you want to mutate something without moving it. Keep your reference lifetimes only as short as they need to be. If the lifetime exceeds multiple scopes, I'd see if there's a better way to do what you're doing.

To use the "borrow" concept: Just borrow it, mutate it, and then put it right back. Don't take it with you when you know that someone else might soon need it. If someone else won't soon need it, then don't borrow it: Take it with you by moving it. Moves in rust are cheap, and often don't even compile as an actual move, especially for anything that is already heap allocated, where they always compile into a pointer. You might be used to C++ where passing without an explicit pointer is often expensive, but that just isn't the case in rust.

Comment Re:I don't currently use Rust (Score 1) 161

There are quite a few things in there that I would avoid doing in general. For example, one of the match conditions mutates a value when it's doing the check, i.e.

None if map.recently_evicted(id) => Err(format!("{id} recently evicted")),

This is where your problem mostly lies. While I never studied computer science, I learned something early on: You generally shouldn't be doing things like this in any language, purely as a matter of soundness. For example, using the assignment operator within an if check in C -- not only is it confusing while skimming through code because the = vs == convention, but the code is inherently something you don't want to reuse or else you'll potentially get inconsistent results across multiple passes. Exactly why it's now frowned upon by experienced C developers, even though it's part of the standard that they value so much, and there's no getting rid of it. Ever. Likewise, for rust, I wouldn't match against an &mut, because then all of your matches must also be &mut, complicating things further, not least of which because &T is always Copy, where &mut T is never Copy, making the former great for doing checks, and the latter very poor for it.

I wouldn't "if" against &mut either, and for the same reason. Generally when I create struct methods, especially in traits, I start with immutable first, and then introduce mutables as they're needed. In other languages when you're multithreading, this helps avoid data races. In rust, it helps you avoid pissing off the borrow checker. You can do it, but eh...you probably shouldn't. Regardless of language, rust or not, even if it's a fully garbage collected, dynamically typed, easy as pie language, this will inevitably lead to confusion and bugs the more you build on top of it. In a roundabout way, the borrow checker is telling you that you really shouldn't be going down this road.

John Carmack did this amazing writeup about this all the way back in 2010, well before most people even heard of rust (and about 10 years before I ever even did any programming at all) and this, along with another paper he wrote, almost reads like a prophecy that some day somebody would invent a language like rust. I *highly* recommend reading it, it's not long at all.

https://web.archive.org/web/20...

Almost certainly owing to the fact that rust is basically the first language I spent more than 3 consecutive months working with, what he's saying here really came naturally to me long before I found his writeup. The nice thing about rust is you can break the rules he wrote in that bit while avoiding undefined behavior, you can certainly code your way into a corner where the benevolent dictator for life, aka the borrow checker, will want a word. Like he says, have fewer moving parts, and it helps you stay out of trouble. In this context, your hashmap is clearly your "state". Mutating the state inside the "if" inherently involves more moving parts than what should just be simply reading from a value to determine what to do next, i.e. your control flow.

If you *really* want to do it this way, I'd wrap it in a refcell, which gives you interior mutability without a runtime penalty, though it's way less ergonomic, and it's not Sync, which means you can't multithread that way (for that, you'd want e.g. Arc<RwLock<T>>, which is also Send, which also allows you to freely pass ownership between threads. I personally have only ever encountered one situation where I absolutely needed a refcel, and it was basically in a spot where I didn't want to clone a big vector.

But still, I just wouldn't. Depends on what you're doing. Try this:

https://play.rust-lang.org/?ve...

Notice also how your "works" is the only one that isn't trying to make decisions from a mutable reference, regardless of whether it's going to mutate AND decide at the same time.

Comment Re: Why do we need a giant publicly funded moon b (Score 1) 81

IIRC Kennedy originally wanted to do something spectacular to show the world how advanced the US was, and things like desalinating water were considered. But he also wanted to improve relations with the USSR

This doesn't sound like something Kennedy would do. You do know he provoked the Cuban Missile Crisis and was ultimately the one who committed the United States into Vietnam, right? A lot of people like to pin that on either Johnson or Nixon, but it was in fact Kennedy who ordered boots on the ground and ordered the CIA to manipulate local elections, the overthrow oh the government it originally put into place, ultimately leading to the assassination of Ngo Dinh Diem.

Either way, there's no way in hell the USSR would have done that. Even if Khrushchev was somehow on board, good luck selling that to the Russian politburo at the time in that pretend union of republics: The prevailing sentiment among Americans and the soviets alike was that the US was losing the cold war, even though in retrospect neither the US nor Russia had any idea just how poorly Russia's economy was doing. Either way, why would they give up their lead while everybody thinks they're ahead just to get better gains later? That would be very un-Russian, even today, and you know it.

Comment Re:I don't currently use Rust (Score 1) 161

If you're going to shit on a language, do it to C++. C developers, even if they don't use rust, typically understand why it exists and why it's needed, even where they see C++ as being a worse than useless dumpster fire.

C++ developers usually hate C developers because C developers don't buy into the same crap that C++ does. C++ developers hate rust because it completely outclasses their language in Every. Single. Way. without anywhere near the complexity. They inevitably fall back into the "the world needs C++ because legacy code!" argument, yet they also contradict themselves by denying that they're currently sitting where COBOL was in the 00s, or that C++ looks to C and Rust developers like COBOL looks to C++ developers: Quaint and overengineered.

Comment Re:I don't currently use Rust (Score 1) 161

As long as there is any path through the function that returns a mutable ref to a value in the map, the compiler considers all subsequent paths to be holding a mutable ref, even if they're not reachable if the ref-returning path executes.

Hmm...that hasn't been my experience in rust since at least 2023. I don't recall exactly when, but the borrow checker got a lot smarter about this kind of thing, particularly being able to tell whether you're borrowing again while still borrowed within the same scope. At the very least, mutable refs, like all references with a non-static lifetime, only live as long as the declaring scope of the variable holding them, never longer. That scope can be a function, but it can also be much narrower, i.e. within just *any* set of curly brackets. Was the &mut dropped by the end of the scope within that match block? If not explicitly so, i.e. the scope lifetime of the &mut isn't entirely within a set of curly brackets, and the variable isn't ever used outside of that, if a variable is holding the &mut to begin with. If not, try assigning that &mut to a variable, then drop() it after just to see. If nothing else, the compiler will tell you and even visually show you exactly where it's being used.

I abused the borrow checker pretty good here, still compiles: https://play.rust-lang.org/?ve...

Comment Re:I don't currently use Rust (Score 1) 161

breaking a hashmap lookup into two steps.

Have you used the map.entry(&T) methods? i.e. or_insert, or_default, etc. Basically, if the entry isn't already initialized, it does so, then yields either what you specified to insert, or the default. Those ended up solving all of my borrow checker issues with hashmaps. A lesson I learned very early in my rust days:

https://stackoverflow.com/ques...

They also make it much easier to to deal with the "vector inside of each hashmap entry needs to be initialized" situation a hell of a lot easier than you've ever done it in any other language before. An interesting side effect of the borrow checker is the rust developers got a strong desire to make the language ergonomic, and this was a badly needed pattern in every language that you never knew you wanted.

The borrow checker keeps paying insane dividends. It was built for memory safety, but gave us incredibly easy concurrency and parallelism, and stuff like this. This is why I keep telling people that the memory safety aspect of rust is merely the cherry on top, the main course of the meal is making doing things the right way the easy way.

Slashdot Top Deals

All science is either physics or stamp collecting. -- Ernest Rutherford

Working...