1

To summarize, I post a simple C++ example that illustrates my question. I want to write the equivalent in Rust. I have read the documentation, but I cannot find the precise answer to this.

#include <iostream>
using namespace std;

int _i; 

void firstFunction() {
    _i++;
}

void secondFunction() {
    _i++;
}

int main()
{
    firstFunction();
    secondFunction();
    cout << "Value of _i is: " << _i; // 2 (yes!)
}

My attempt at Rust

let mut _i:i32 = 0; // ?

fn firstFunction() {
    _i = _i + 1;
}

fn secondFunction() {
    _i = _i + 1;
}

fn main() {
    firstFunction();
    secondFunction();
    println!("Value of _i is {}", _i); // must be 2
}

Answering my question, after the knowledge acquired by your excellent answers, I have written this approach, which seems closer to what I wanted to find,

struct Something {
    counter: i32,
}

impl Something {
    fn first_function(&mut self) {
        self.counter = self.counter + 1;
    }
    fn second_function(&mut self) {
        self.counter = self.counter + 1;
    }
}

fn main() {
    println!("Learning");

    let mut _t = Something { counter: 0 };
    _t.first_function();
    _t.second_function();

    println!("t: {}", _t.counter);
}
  • Your intent is not clear. You can create a struct that contains that variable and then f1 and f2 would be methods of that structure. Or you can make a local variabl ein main and then pass references to f1 and f2. Or you can make a global static variable, wrapped in a Mutex for access control. There are a lot of options, but it depends on what you want to achieve. – Svetlin Zarev Aug 22 '21 at 19:15
  • In rust your code would be considered unsafe (in general across all platforms that rust could compile to), so you won't be able to do it as simply as you want. There are some various idiomatic ways with the simplest for integer being use atomics, but this question gives multiple different ways to approach: https://stackoverflow.com/questions/27791532/how-do-i-create-a-global-mutable-singleton – Jere Aug 22 '21 at 21:03
  • This approach "How do I create a global, mutable singleton?", it is very interesting. My C++ example is more for understanding than for a solution. Certainly in all languages the matter is simple, and I have learned a lot from your comments. TXS – Alexandra Danith Ansley Aug 22 '21 at 22:11
  • It is a possible solution, but it takes it to such a different level that it moves away from the intention of the original question, looking for an intuitive approach between the two languages. – Alexandra Danith Ansley Aug 23 '21 at 16:16
  • Why did you mark your question as a duplicate? I do not think – Sith2021 Aug 23 '21 at 16:46

1 Answers1

3

In general, global variables should be avoided (not only in Rust), and Rust makes their usage very difficult (on purpose, think about mutable borrows issues!). However, this page illustrates some ways to play around.

The intent of your C++ example is not clearly exposed, but you can achieve something similar with the example below:

  • the global variable is made local to main(),
  • the functions are converted to closures in order to capture this variable,
  • closure definitions and invocations are reorganised in order to prevent from multiple simultaneous mutable borrows of the variable.
fn main() {
    let mut _i: i32 = 0;
    let mut first_function = || {
        _i = _i + 1;
    };
    first_function();
    let mut second_function = || {
        _i = _i + 1;
    };
    second_function();
    println!("Value of _i is {}", _i); // must be 2
}

edit: this ugly solution insists on using a global variable in order to be closer to the original C++ code; this is absolutely not an example to be followed.

static mut _I: i32 = 0;

fn get_i() -> i32 {
    unsafe { _I }
}

fn set_i(i: i32) {
    unsafe { _I = i };
}

fn first_function() {
    set_i(get_i() + 1);
}

fn second_function() {
    set_i(get_i() + 1);
}

fn main() {
    first_function();
    second_function();
    println!("Value of _i is {}", get_i()); // must be 2
}

edit: as stated in the remark before the previous example and in the comments, the previous example is close to the C++ code but is totally incorrect regarding Rust expectations (no synchronisation if we were in a multithreaded context). A step towards a more Rust-correct solution, but away from the original C++ example, would be the following one. However, it is difficult to know what kind of answer is expected here: something that looks like C++ but that does not conform to Rust expectations, or something that would eventually be acceptable in Rust even if it does not look like the original C++ code...

// in Cargo.toml
//   [dependencies]
//   lazy_static = "1.4.0"
#[macro_use]
extern crate lazy_static;

use std::sync::Mutex;

lazy_static! {
    static ref _I: Mutex<i32> = Mutex::new(0);
}

fn first_function() {
    let mut i = _I.lock().unwrap();
    *i = *i + 1;
}

fn second_function() {
    let mut i = _I.lock().unwrap();
    *i = *i + 1;
}

fn main() {
    first_function();
    second_function();
    println!("Value of _i is {}", _I.lock().unwrap()); // must be 2
}
prog-fh
  • 13,492
  • 1
  • 15
  • 30
  • The purpose of the simple example is to illustrate the flow. The code that you suggest to me has a quite heterodox order and flow with what I thought. Hopefully there is a more similar way :_) – Alexandra Danith Ansley Aug 22 '21 at 19:34
  • The second approach is closer. In fact I learned several things. – Alexandra Danith Ansley Aug 22 '21 at 22:04
  • In the second example all the functions (except `main`) should be marked `unsafe` because, as written, they don't provide a safe abstraction over the underlying unsafe functionality. Their unsafe contract is that one is not allowed to call them from multiple threads without synchronization. – user4815162342 Aug 23 '21 at 07:02
  • Re edit: Making the _functions_ in the second example as unsafe would make it totally conform to Rust's safety expectations, though it would still not be idiomatic Rust. As written, it's just unsound. – user4815162342 Aug 23 '21 at 16:05