Fernando JRR
Fernando JRR

Reputation: 21

"mismatched types expected unit type `()` found enum `Option<String>`" Error when trying to read the content of a file with Tauri

I was trying to use the file picker of tauri to read the contents of a file in Rust this way.

fn open_file() -> Option<String> {
    let dialog_open_file = tauri::api::dialog::FileDialogBuilder::new();
    dialog_open_file.pick_file(|path| {
    match path {
      Some(directory) => {
          let archivo = fs::read_to_string(directory);
          match archivo {
              Ok(content) => { return Some(content); },
              Err(error) => { return None; }
          }
      },
      None => { return None; }
    }
  });
}

The problem is that when I try to return Some(content) I get the error "mismatched types expected unit type () found enum Option<String>"

error[E0308]: mismatched types
  --> src/main.rs:25:43
   |
25 |                   Ok(content) => { return Some(content); },
   |                                           ^^^^^^^^^^^^^ expected `()`, found enum
   = note: expected unit type `()`
               found enum `Option<String>`


error[E0308]: mismatched types
  --> src/main.rs:18:19
   |
18 | fn open_file() -> Option<String> {
   |    ---------      ^^^^^^^^^^^^^^ expected enum `Option`, found `()`
   |    |
   |    implicitly returns `()` as its body has no tail or `return` expression
   |
   = note:   expected enum `Option<String>`
           found unit type `()`

I have tried many ways but I can't return the Option out of the function "dialog_open_file" to make the function return something.

Upvotes: 2

Views: 5548

Answers (3)

jthulhu
jthulhu

Reputation: 8678

Edit

This post does not work for the OP question, because tauri's API requires the closure to be 'static, and thus rules out the possibility (except with dirty unsafe hacks) to retrieve the content of the file in a way similar to what was asked.

Also, tauri's API has been changed in more recent versions. If you are only interested in a solution that specifically works with tauri, please see @Michael Anderson's answer.


As you can see, there are two errors, which are opposite, in some way: one error tells you that Rust expected (), but found Option<String>, and the other one that it expected Option<String>, but found (). This should prompt you to check if you are passing you Option<String> at the right place and, in fact, you're not.

A closure is a function (not quite the same as other functions you define with fn, but still a function), so when you call return in it, you're actually returning inside the closure. So, the error just comes from the fact that pick_file expected a function that returns (), whereas you provided one that returns Option<String>, and conversely the outer function is (implicitly) returning ().

You may ask: is there a way to return from inside the closure, but for the return statement to apply to the outside function, and the answer is no, so one way would be to create a variable outside and mutate it from inside the closure:

fn open_file() -> Option<String> {
    let dialog_open_file = tauri::api::dialog::FileDialogBuilder::new();
    let mut result = None;
    dialog_open_file.pick_file(|path| {
        if let Some(directory) = path {
            let archivo = fs::read_to_string(directory);
            if let Ok(content) = archivo {
                result = Some(content);
            }
        }
    });
    result
}

Upvotes: 1

Michael Anderson
Michael Anderson

Reputation: 73570

It looks like tauri has changed its API in this area. See https://docs.rs/tauri-api/0.7.6/tauri_api/dialog/index.html. Now the functions no longer accept a closure, but instead return a dialog::Response struct.

This means your code could be written as something like:

// This is tauri's internal error type too.
use anyhow::{Error, bail};

fn open_file_impl() -> Result<String, Error> {
    use tauri::api::dialog::Response;
    let result = tauri::api::dialog::select(None,None)?;
    
    let path = match {
        Response::Okay(s) => s;
        Response::OkayMultiple(s) => bail!("multiple selected"),
        Response::Cancel => bail!("canceled");
    }
    Ok(fs::read_to_string(directory)?)
}

pub fn open_file() -> Option<String> {
   open_file_impl().ok()
}

I'd probably introduce a concrete error type with this, rather than relying on anyhow... but that does make things longer.

Or you could get rid of the open_file_impl altogether...

fn open_file_impl() -> Option<String> {
    use tauri::api::dialog::Response;

    let result = tauri::api::dialog::select(None,None).ok()?;
    if let Response::Okay(path) = result {
        fs::read_to_string(directory).ok()
    } else {
        None
    }
}

Upvotes: 1

ramsay
ramsay

Reputation: 3855

If you format your code, you would find out that you are missing the return statement in the outer space.

fn open_file() -> Option<String> {
    let dialog_open_file = tauri::api::dialog::FileDialogBuilder::new();
    let mut result = None;
    dialog_open_file.pick_file(|path| match path {
        Some(directory) => {
            let archivo = fs::read_to_string(directory);
            match archivo {
                Ok(content) => result = Some(content),
                Err(error) => result = None,
            }
        }
    });
    result
}

Upvotes: 0

Related Questions