hallettj

joined 2 years ago
[–] hallettj 11 points 11 months ago (2 children)

This is exactly why we have Reversed Polish Notation. When will people learn?

[–] hallettj 1 points 11 months ago

Oh goddammit! Why doesn't PEMDAS prepare us for unary negation??

[–] hallettj 2 points 11 months ago (1 children)

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.

[–] hallettj 3 points 11 months ago (1 children)

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.

[–] hallettj 2 points 11 months ago (5 children)

Oh I'm sorry! I messed up my test case so it only looked like the block fixed things.

[–] hallettj 7 points 11 months ago (4 children)

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).

[–] hallettj 1 points 11 months ago (7 children)

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.

[–] hallettj 3 points 11 months ago* (last edited 11 months ago) (1 children)

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.

[–] hallettj 1 points 11 months ago (9 children)

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

[–] hallettj 1 points 11 months ago* (last edited 11 months ago)

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:

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=5bd6fb7c1cbf1c9c44c8f4bdbb1e8074

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:

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=491258a7dc9bcad9dab08632d68c026b

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.

[–] hallettj 1 points 1 year ago* (last edited 1 year ago) (2 children)

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.

[–] hallettj 2 points 1 year ago

Thank you for including a workaround with this terrible news!

view more: ‹ prev next ›