2

I'm pretty new to Rust, and I'm trying to understanding the core concept of it which is ownership and borrowing. I already read the book and some other articles, but it still confuses me.

I have a struct defined like this:

#[repr(C)]
#[derive(Copy, Clone)]
pub struct Element(pub (crate) [u64; 12]);

I have an external function which swaps the values of two Element based on the flag value. If flag is 0, they are not swapped, and if flag is 1, they are swapped. That's why in the prototype I use &mut:

extern {
    pub fn swap(x: &mut Element, y: &mut Element, flag: u8);
}

I create a wrapper method in Rust around this external function:

impl for Element {
    fn wrapper_swap(&mut self, y: &mut Element, flag: u8) {
        unsafe {
            swap(self, y, flag); // OR swap(&mut self, &mut y, flag); ?!
        }
    }
}

My wrapper_swap function has the same signature as the external prototype function swap. How do I need to call the external function swap here?

Since self and y already are defined as &mut self and &mut y, can I just call the swap function as swap(self, y, flag);? Or will that remove the mutable references, and actually I need to call it as swap(&mut self, &mut y, flag);?

It also gets complicated for me as to how I call it with actual values. For example, I have three variables defined like this:

let mut one = Element([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
let mut two = Element([2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]);
let flag: u8 = 0;

Since the variables are already defined as mutable, do I need to call the wrapper_swap function as (&one).wrapper_swap(&two, flag); or will this make references immutable no matter that the variables are defined as mutable, and instead I need to call it as (&mut one).wrapper_swap(&mut two, flag);? Which is the correct approach?

Maybe it's because I'm using unsafe functions, but the compiler does not complain about how I call this function.

What would happen if the variables one and two were not defined as mutable? Can I still borrow them mutable as &mut one or &mut two, and change their value in another function?

tinker
  • 2,884
  • 8
  • 23
  • 35
  • 2
    [**One** question per post, please](https://meta.stackexchange.com/q/39223/281829). – Shepmaster Dec 25 '17 at 19:17
  • 2
    See also [Can an FFI function modify a variable that wasn't declared mutable?](https://stackoverflow.com/q/47620011/155423) – Shepmaster Dec 25 '17 at 19:29
  • 1
    For what it's worth, your question doesn't really have anything to do with ownership or borrowing, both of which usually refer to how long a given value is valid. – Shepmaster Dec 25 '17 at 19:33
  • 1
    @Shepmaster Thanks, and sorry for multiple questions. I checked the question that you have linked. Since, I do not have enough reputation to comment there, I will ask here. In the question that you have linked I see that the OP asks modifying a parameter that is marked as `const` inside the function. But, what I see in his `main` function is that the `val` variable is also not defined as `mut`. Now, what I don't understand is what is the proper way to call it? Just `foo(&val)` or `foo(&mut val)` assuming `val` is not `mut`, or to define the `val` as `mut` and do `foo(&val)` or `foo(&mut val)`? – tinker Dec 25 '17 at 19:50

1 Answers1

2

You could have answered your question with a minimal amount of exploration. Don't be afraid to try things when programming; you are usually very unlikely to lose anything of value.

For example...

build.rs

extern crate cc;

fn main() {
    cc::Build::new()
        .file("src/swap.c")
        .compile("repro");
}

src/swap.c

#include <stdio.h>
#include <stdlib.h>

void swap(int32_t *a, int32_t *b) {
  printf("Swapping %p, %p\n", a, b);

  int32_t t = *a;
  *a = *b;
  *b = t;
}

src/main.rs

extern crate libc;

use libc::int32_t;

extern "C" {
    fn swap(a: &mut int32_t, b: &mut int32_t);
}

fn swap_wrapper_1(a: &mut i32, b: &mut i32) {
    unsafe { swap(a, b) }
}

fn main() {
    let mut a = 1;
    let mut b = 2;

    swap_wrapper_1(&mut a, &mut b);
    println!("{}, {}", a, b);
}

This prints:

Swapping 0x7ffee1cf9ff0, 0x7ffee1cf9ff4
2, 1

If you attempt to use your second form:

fn swap_wrapper_2(a: &mut i32, b: &mut i32) {
    unsafe { swap(&mut a, &mut b) }
}

You get a bunch of warnings:

error[E0596]: cannot borrow immutable argument `a` as mutable
  --> src/main.rs:15:15
   |
14 | fn swap_wrapper_2(a: &mut i32, b: &mut i32) {
   |                   - consider changing this to `mut a`
15 |     swap(&mut a, &mut b)
   |               ^ cannot borrow mutably

If you fix that and then call both forms back-to-back, you get:

Swapping 0x7ffee7483f60, 0x7ffee7483f64
2, 1
Swapping 0x7ffee7483f60, 0x7ffee7483f64
1, 2

That's right — it's swapping the exact same addresses. That's because Rust will automatically dereference your value when it needs to to match the signature, so it doesn't matter as long as you've defined the FFI signature correctly.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366