0

I am testing some Rust code and want to log some of the intermediate data to a file to make sure it is correct before I write the next element in the pipeline that consumes the data. In any other language I'd just write the data to a file handle stored in a static variable, but rust complains (correctly) that this is not thread safe. Since the application in question uses gstreamer this is theoretically a threat. I'll just paste my naive version below:

use std::fs::File;
use std::io::Write;

fn main() {
    log_mesh("bacon");
}

static mut meshes : Option<Result<File, std::io::Error>> = None;

fn log_mesh(message: &str)
{
    if let None = meshes {
        meshes = Some(File::open("/tmp/meshes.txt"));
    }

    if let Ok(mut f) = meshes.unwrap() {
        f.write_all(message.as_bytes());
        f.flush();
    }
}

This results in two important kinds of compile errors:

error[E0507]: cannot move out of static item
  --> src/main.rs:16:24
   |
16 |     if let Ok(mut f) = meshes.unwrap() {
   |                        ^^^^^^ cannot move out of static item

error[E0133]: use of mutable static is unsafe and requires unsafe function or block
  --> src/main.rs:12:19
   |
12 |     if let None = meshes {
   |                   ^^^^^^ use of mutable static
   |
   = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior

I have flailed around with Mutex, lazy_static!, mut_static, but all of them lead me into the woods and I get lost. I assume there has to be some compact idiom to solve this problem that isn't coming up in Google search results.

Mutant Bob
  • 3,121
  • 2
  • 27
  • 52
  • 1
    Possible duplicate of [How do I create a global, mutable singleton?](https://stackoverflow.com/questions/27791532/how-do-i-create-a-global-mutable-singleton) – Stargateur Jul 18 '19 at 18:05
  • the global mutable singleton answer does not directly lead to working code. There are extra techniques needed to make it work. – Mutant Bob Jul 18 '19 at 20:44

1 Answers1

0

The first attempt at lazy_static! usage would be

use lazy_static::lazy_static;
use std::sync::Mutex;

use std::fs::File;
use std::io::Write;


lazy_static! {
static ref meshes : Mutex<Result<File, std::io::Error>> = Mutex::new(File::open("/tmp/meshes.txt"));
}

pub fn log_mesh(message: &str)
{
    let mut tmp = meshes.lock().unwrap();

    if let Ok(ref mut f) = tmp {
        f.write_all(message.as_bytes());
        f.flush();
    }
}

which triggers this compile error:

error[E0308]: mismatched types
  --> src/module2.rs:16:12
   |
16 |     if let Ok(ref mut f) = tmp {
   |            ^^^^^^^^^^^^^   --- this match expression has type `std::sync::MutexGuard<'_, std::result::Result<std::fs::File, std::io::Error>>`
   |            |
   |            expected struct `std::sync::MutexGuard`, found enum `std::result::Result`
   |
   = note: expected type `std::sync::MutexGuard<'_, std::result::Result<std::fs::File, std::io::Error>, >`
              found type `std::result::Result<_, _>`

which is somewhat discouraging and chock-full of behind-the-curtain magic, but can be solved by changing it to

    if let Ok(ref mut f) = *tmp {

I hesitate to mark it as an answer because there could be race conditions a more experienced coder can spot or another idiom that is superior.

Mutant Bob
  • 3,121
  • 2
  • 27
  • 52
  • more simply: https://play.integer32.com/?version=stable&mode=debug&edition=2018&gist=a5272e90d395c508ed5d5c82bfcc1d21 – Stargateur Jul 18 '19 at 21:26
  • There ended up being a couple of ways to arrange the `if`, like `if let Ok(mut f) = tmp.as_mut() {` . I'm not sure if there's a stylistic ranking for them. I often introduce a variable so I can see how IntelliJ guesses the type and then explicitly declare the type to help me understand what is going on and give me access to the source in the crate through IntelliJ's Go To>Declaration ability. It is like the ancient trade-off of compactness for "readability". – Mutant Bob Jul 19 '19 at 13:38