There's nothing tricky / magical in this code:
fn next_id(&mut self) -> u64 {
let nfi = self.next_free_id;
self.next_free_id = nfi + 1;
nfi
}
It just seems slightly verbose. Is there a way to go from 3 lines to 2 (or maybe 1) ?
2 Likes
Here's a two-line version, though I'm not sure it's an improvement:
fn next_id(&mut self) -> u64 {
let next = self.next_free_id + 1;
std::mem::replace(&mut self.next_free_id, next)
}
Another questionable version:
fn next_id(&mut self) -> u64 {
self.next_free_id += 1;
self.next_free_id - 1
}
If you store the previous ID instead of the next ID, that one simplifies to:
fn next_id(&mut self) -> u64 {
self.prev_id += 1;
self.prev_id
}
3 Likes
C has something like n ++
which (1) returns the old value of n
and (2) also increments n
.
Is there nothing like this in Rust ?
1 Like
Please take a look at the following thread a year or so ago which discusses why/why not this is in rust, and the pros/cons about [post|pre][in|de]crement operators
.
x++;
Why is this invalid, other languages allow you to increment a variable by one like this, why can't you do this in Rust?
3 Likes
cuviper
February 17, 2020, 1:21am
6
I've abused tuples for this before , but it looks even worse with long identifiers:
(self.next_free_id, self.next_free_id += 1).0
12 Likes
jimuazu
February 17, 2020, 4:59pm
7
With a Pony-like "replace" assignment operator (=
in Pony, write it as <-
here) that returns the old value from an assignment, it could be:
fn next_id(&mut self) -> u64 {
self.next_free_id <- self.next_free_id + 1
}
2 Likes
alice
February 17, 2020, 5:59pm
8
How about mem::replace
?
fn next_id(&mut self) -> u64 {
let next_id = self.next_free_id + 1;
mem::replace(&mut self.next_free_id, next_id)
}
2 Likes
I’ve wanted this in Rust for a long time.
1 Like
Here's the solution I ended up going with:
use super::*;
pub trait U64UtilT {
fn n_pp(&mut self) -> u64;
fn pp_n(&mut self) -> u64;
}
impl U64UtilT for u64 {
fn n_pp(&mut self) -> u64 {
let old = *self;
*self = *self + 1;
return old;
}
fn pp_n(&mut self) -> u64 {
*self = *self + 1;
return *self;
}
}
@alice : Isn't this missing a return ?
@cuviper : I'm not sure if this is interesting or syntax abusive. I'm not familiar with Rust evaluation order. Does it guarantee that tuples are evaluated left to right?
cuviper
February 18, 2020, 1:37am
13
AFAIK it's not strictly specified, but "the ship has sailed", as Niko put it in this thread:
Not that I know of. I would like to get more progress on a reference of this kind. Roughly speaking, the order is left-to-right, though in an assignment l = r, the expression l is evaluated second.
2 Likes
ExpHP
February 18, 2020, 4:25pm
14
How about one line?
struct Struct {
unused_ids: std::ops::RangeFrom<u64>,
}
impl Struct {
fn next_unused_id(&mut self) -> u64 {
self.unused_ids.next().unwrap()
}
}
28 Likes
It's probably the extra ;
at the end that @alice added by accident. The return
keyword is typically used for early returns in rust - something like
fn foo(...) -> Result<(),()> {
if x == 5 {
return Err(())
}
do_stuff();
do_more_stuff();
do_other_stuff();
Ok(())
}
1 Like
@eugene2k : Interesting, I didn't realize mem::replace returned a T
alice
February 19, 2020, 2:14pm
17
That's, like, it's whole thing! Otherwise it would be the same as an ordinary assignment.
2 Likes
hellow
February 19, 2020, 2:26pm
18
For everyone who wonders if that's a good approach. Yes it fucking is! This boils down to three instructions:
example::Struct::next_unused_id:
movq (%rdi), %rax
leaq 1(%rax), %rcx
movq %rcx, (%rdi)
retq
no jump, no panic, just what you would expect and what a hand written + 1
would do. Rust is so amazing when it comes to zero cost abstractions.
18 Likes
system
Closed
May 19, 2020, 2:26pm
19
This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.