Oh goddammit! Why doesn't PEMDAS prepare us for unary negation??
hallettj
The problem is that the way PEMDAS is usually taught multiplication and division are supposed to have equal precedence. The acronym makes it look like multiplication comes before division, but you're supposed to read MD and as one step. (The same goes for addition and subtraction so AS is also supposed to be one step.) It this example the division is left of the multiplication so because they have equal precedence (according to PEMDAS) the division applies first.
IMO it's bad acronym design. It would be easier if multiplication did come before division because that is how everyone intuitively reads the acronym.
Maybe it should be PE(M/D)(A/S). But that version is tricky to pronounce. Or maybe there shouldn't be an acronym at all.
The parentheses step only covers expressions inside parentheses. That's 2 + 2 in this case. The times-2 part is outside the parentheses so it's evaluated in a different step.
Oh I'm sorry! I messed up my test case so it only looked like the block fixed things.
The comment from subignition explains that the phone's answer, 16, is what you get by strictly following PEMDAS: the rule is that multiplication and division have the same precedence, and you evaluate them from left-to-right.
The calculator uses a different convention where either multiplication has higher priority than division, or where "implicit" multiplication has higher priority (where there is no multiply sign between adjacent expressions).
Did you try adding the block like I suggested? I reproduced the same error that you are seeing, and verified in my playground snippet that the block fixes the problem.
It's ok that query_map references pstmt. Isolating pstmt in a block causes it to be dropped at the end of the block while rec_iter continues to live. I think that might be thanks to temporary lifetime extension or something.
I want to make a small correction - this is not true:
iirc I had to reboot every time for it to be applied while with Arch you can just install something and run it immediately.
nixos-rebuild
behaves like most package managers: it makes new packages available immediately, and restarts relevant systemd services. Like other distros you have to reboot to run a new kernel.
And cleaning up Steam games is as issue with most distros too. But I kinda see your point.
Btw Nix (both NixOS and the Nix package manager running in other distros) has this feature where you can run packages without "installing" them if you just want to run them once:
$ nix shell nixpkgs#package-name
That puts you in a shell with one or more packages temporarily installed. The packages are downloaded to /nix/store/
as usual, but will get garbage-collected sometime after you close the shell.
Ok I have a simple solution for you. What you need to do is to produce rec_iter
in such a way that pstmt
is dropped at the same time. You can do that by creating and calling pstmt
in a block like this:
let rec_iter = {
let mut pstmt = conn.prepare(sql.as_str())?;
pstmt.query_map(/* ... */)?
};
Here is a Rust Playground example
I'm glad you found a workaround. I didn't want to be defeated by the callback idea that I had yesterday so I worked on it some more and came up with a similar-but-different solution where ZnsMaker
stores pstmt
paired with a closure that borrows it. This is again a simplified version of your code that is self-contained:
The closure solution avoids the need to pass conn
to make_stream
. I don't know if it would fix the new borrowing error that you got since I did not reproduce that error. But I think it might.
Does znsstream
need to outlive znsm
? If so I think my solution solves the problem. Does znsstream
need to outlive conn
? That would be more complicated.
Edit: Oh! You don't need to put both the pstmt
and a closure in ZnsMaker
. Instead you can just store a closure if you move ownership of pstmt
into the closure. That ensures that pstmt
lives as long as the function that produces the iterator that you want:
Edit: Lol you don't need ZnsMaker
after all. See my other top-level comment. I learned some things working on this, so time well spent IMO.
Well I'm curious to know what solutions there are to this. But I did reduce your problem to a smaller, self-contained example:
struct ZKNoteStream<'a, T> {
rec_iter: Box + 'a>,
}
struct BorrowedThing(Vec);
impl BorrowedThing {
fn prepare(&self) -> IterProducer<'_> {
IterProducer(&self.0)
}
}
struct IterProducer<'a>(&'a Vec);
impl<'a> IterProducer<'a> {
fn query_map(&self) -> impl Iterator {
self.0.into_iter()
}
}
fn test(conn: &BorrowedThing) -> ZKNoteStream<'_, &String> {
let pstmt = conn.prepare();
let rec_iter = pstmt.query_map();
ZKNoteStream { // cannot return value referencing local variable `pstmt`
rec_iter: Box::new(rec_iter),
}
}
Edit: Wow, that code block came out mangled. Here is a Rust Playground link
I ran into a similar problem yesterday, but it was a case where I had control over all the code. What I did was the equivalent of changing query_map
so that instead of taking a reference to its receiver, it takes ownership.
My thinking at the moment is that you may need to modify ZKNoteStream
so that instead of containing the resulting iterator it contains pstmt
, maybe paired with a function takes pstmt
as an argument and returns the iterator. It seems like you should be able to create an FnOnce
closure with a move
keyword that moves ownership of pstmt
into that closure, which you could then store in ZKNoteStream
. But I'm not able to make that happen right now.
Thank you for including a workaround with this terrible news!
This is exactly why we have Reversed Polish Notation. When will people learn?