Annotating types with `for`

This code works (stmt is a rusqlite::Statement):

let blah: Vec<(u64, String)> = stmt
	.query_map([], |row| Ok((row.get(0)?, row.get(1)?)))?
	.collect::<std::result::Result<_, _>>()?;

for (id, name) in blah {
	println!("{:#?}", id);
	println!("{:#?}", name);
}

Playground

I would like to inline blah. I thought I could annotate the type like this: for (id, hash): (u64, String) in ... but apparently not?

I also suspect that the collect() might load the whole result into memory? In that case I would also like to get rid of it, but where would the error handling go that it provides?

1 Like

The "type ascription everywhere" feature got removed and I'm unaware of a champion for a replacement.

This works for your example:

    for (id, name) in stmt
    	.query_map([], |row| Ok((row.get(0)?, row.get(1)?)))?
    	.collect::<std::result::Result<Vec<(u64, String)>, _>>()?
        //                             ^^^^^^^^^^^^^^^^^^

Could you not also specify the types on the construction of the Ok, as well? Something like Ok::<(u64, String), _>((row.get(0)?, row.get(1)?))?

What I'm not sure of is how to get the "coercion" into Result<Iter<_>, _> that the original post wants (I think) and that collect provides when you call it on an iter of Result. Nor am I sure how to do that; it seems like you would have to either iterate over the collection twice, looking for failures, before you started returning values; or allocate space, as collect::<Vec> does

1 Like

That covers the type of the Result, yes. Not of the collection, but...

...yeah, my bad, I read the first paragraph, "fixed" the playground, and failed to grok they (probably) didn't mean literally inline the same behavior.


You could do this

    for res in stmt
    	.query_map([], |row| Ok::<(u64, String), _>((row.get(0)?, row.get(1)?)))?
    {
        let (id, name) = res?;

Through I prefer this:

    for res in stmt.query_map([], |row| Ok((row.get(0)?, row.get(1)?)))? {
        let (id, name): (u64, String) = res?;

It is a change of behavior though, because now if you have an Err halfway through, you'll have executed the loop for everything before you reached the Err. If that's not acceptable, you need the collect or something basically equivalent.

3 Likes

That looks quite nice indeed, I'll use it. The change in behavior is fine, welcome even, the all-or-nothing behavior of collect() was more of a conincidence.

By the way, this works as well:

for res in stmt.query_map([], |row| Ok((row.get::<_, u64>(0)?, row.get::<_, String>(1)?)))? {
	let (id, name) = res?;
	println!("{:#?}", id);
	println!("{:#?}", name);
}
1 Like