How to swap two variables?

后端 未结 3 1242
孤城傲影
孤城傲影 2020-12-01 13:55

What is the closest equivalent Rust code to this Python code?

a, b = 1, 2
a, b = b, a + b

I am trying to write an iterative Fibonacci funct

相关标签:
3条回答
  • 2020-12-01 14:41

    When swapping variables, the most likely thing you want is to create new bindings for a and b.

    fn main() {
        let (a, b) = (1, 2);
        let (b, a) = (a, a + b);
    }
    

    However, in your actual case, there isn't a nice solution in stable Rust. When you do as above, you always create new bindings for a and b, but your case wants to modify the existing bindings. One solution I know of is to use a temporary:

    fn fibonacci(n: u64) -> u64 {
        if n < 2 {
            return n;
        }
        let mut fib_prev = 1;
        let mut fib = 1;
        for _ in 2..n {
            let next = fib + fib_prev;
            fib_prev = fib;
            fib = next;
        }
        fib
    }
    

    You could also make it so that you mutate the tuple:

    fn fibonacci(n: u64) -> u64 {
        if n < 2 {
            return n;
        }
        let mut fib = (1, 1);
        for _ in 2..n {
            fib = (fib.1, fib.0 + fib.1);
        }
        fib.1
    }
    

    In nightly Rust, you can use destructuring assignment:

    #![feature(destructuring_assignment)]
    
    fn fibonacci(n: u64) -> u64 {
        if n < 2 {
            return n;
        }
        let mut fib_prev = 1;
        let mut fib = 1;
        for _ in 2..n {
            (fib_prev, fib) = (fib, fib + fib_prev);
        }
        fib
    }
    

    You may also be interested in swapping the contents of two pieces of memory. 99+% of the time, you want to re-bind the variables, but a very small amount of time you want to change things "in place":

    fn main() {
        let (mut a, mut b) = (1, 2);
        std::mem::swap(&mut a, &mut b);
    
        println!("{:?}", (a, b));
    }
    

    Note that it's not concise to do this swap and add the values together in one step.

    For a very concise implementation of the Fibonacci sequence that returns an iterator:

    fn fib() -> impl Iterator<Item = u128> {
        let mut state = [1, 1];
        std::iter::from_fn(move || {
            state.swap(0, 1);
            let next = state.iter().sum();
            Some(std::mem::replace(&mut state[1], next))
        })
    }
    

    See also:

    • Can I destructure a tuple without binding the result to a new variable in a let/match/for statement?
    • How can I swap in a new value for a field in a mutable reference to a structure?
    0 讨论(0)
  • 2020-12-01 14:46

    In addition, a better way to implement the Fibonacci sequence in Rust is using the Iterator trait:

    // Iterator data structure
    struct FibIter(u32, u32);
    
    // Iterator initialization function
    fn fib() -> FibIter {
        FibIter(0u32, 1u32)
    }
    
    // Iterator trait implementation
    impl Iterator for FibIter {
        type Item = u32;
        fn next(&mut self) -> Option<u32> {
            *self = FibIter(self.1, self.1 + self.0);
            Some(self.0)
        }
    }
    
    fn main() {
        println!("{:?}", fib().take(15).collect::<Vec<_>>());
    }
    

    See The Rust Programming Language chapter on iterators.

    0 讨论(0)
  • 2020-12-01 14:52

    I'm surprised none of the answers mention the XOR-swap:

    fibPrev ^= fib;
    fib ^= fibPrev;
    fibPrev ^= fib;
    

    This is a well-known way to swap two values without using a temporary variable or risking integer overflow.

    Note that with modern optimizing compilers, in most cases it's better from performance standpoint to just use a temporary variable (see link in the comment by trentcl). There are, however, some use cases for the XOR-swap, the linked Wikipedia article provides a decent overview of its pros and cons.

    Interestingly enough, if you were to implement a XOR-swap as a function in rust, you don't need to check if you're trying to swap a variable with itself (in which case you'll end up with it being set to zero due to the properties of a XOR operation). Compare swap implementations in rust:

    fn swap(a: &mut i32, b: &mut i32) {
        *a ^= *b;
        *b ^= *a;
        *a ^= *b;
    }
    

    and C++:

    void swap(int& a, int& b) {
        if (&a == &b) return; // Avoid swapping the variable with itself
        a ^= b;
        b ^= a;
        a ^= b;
    }
    

    If you try to call swap like this in rust:

    let mut a = 42;
    swap(&mut a, &mut a);
    

    You will get a compilation error:

    error[E0499]: cannot borrow `a` as mutable more than once at a time
      --> src/main.rs:27:18
       |
    27 |     swap(&mut a, &mut a);
       |     ---- ------  ^^^^^^ second mutable borrow occurs here
       |     |    |
       |     |    first mutable borrow occurs here
       |     first borrow later used by call
    
    0 讨论(0)
提交回复
热议问题