Newbie Problems (Compiler, Environment, build, strange behavior)

Hello, i am new in this forum, from Germany and i startet with Rust, read books and testet in the rust playground. I installed Rust on my Windows Maschine, installed Virtual Studio Code and some recommended addons.

Then i startet my first project, where i want to replace a Python application i programmed earlier.

Now i have a problem and don't find the reason why. I need to import values from an config file and try to use dotenv.

I installed dotenv with "cargo add dotenv" in my project directory wich leads in a entry in the cargo.toml

[dependencies]
dotenv = "0.15.0"

which should be ok.

Then i did my code, had to fight with errors. Ok, not very surprising for the first program. But one error i find no solution. I reduced the code just on this issue.

use dotenv::dotenv;

fn main() {
    dotenv::dotenv().unwrap();

println!("Hello, world!");

}  

If i let that run in the Visual Studio Code, i receive:

8 | use dotenv::dotenv;
  |     ^^^^^^ maybe a missing crate `dotenv`?
  |
  = help: consider adding `extern crate dotenv` to use the `dotenv` crate

error: aborting due to previous error

If i do a "cargo build" in my directory, i receive:

warning: unused import: `dotenv::dotenv`
 --> src\main.rs:8:5
  |
8 | use dotenv::dotenv;
  |     ^^^^^^^^^^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

warning: `myhome` (bin "myhome") generated 1 warning (run `cargo fix --bin "myhome"` to apply 1 suggestion)
    Finished dev [unoptimized + debuginfo] target(s) in 4.02s

which is clear because its not used yet. But how can it be that cargo and visual studio are different in error messages?

And if i run the compiled program in "target/debug" i receive:

thread 'main' panicked at src\main.rs:16:22:
called `Result::unwrap()` on an `Err` value: Io(Custom { kind: NotFound, error: "path not found" })
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

I googled, used even Chat GPT but found no solution.
Is something wrong with my environment? Where can i search for the problem?
Can you help me?
Thank you in advance

Ralf

how do you run the code? if you use the run|debug code lens by rust-analyzer, it should be the same as cargo run. the code lens is enabled by default. see for example

other generic extensions like "code runner" probably would not work because they doesn't understand the cargo specific metadata.

the dotenv crate look for the env file in the current working directory. if you run the program under the target/debug directory, it will fail and return an Err, and the unwrap() will then panic.

Thank you for your answer. I run the code in Visual Studio Code with the "Run Code" Button.

But I have installed Code Runner, that was recommended, so I guess Visual Studio runs the code with code runner... Is there a better way to run code out of the Visual Studio environment?

I removed dotenv and choosed another solution.

this is my main.rs now:

pub mod mqtt;
pub mod config;

fn main() {

    if let Some(address) = config::get_config_value("myhome.conf", "sql_server", "address") {
        println!("Address found: {}", address);
    } else {
        println!("Address not found");
    }

    println!("Hello, world!");

} 

It ist only one Value for example, but so i can read my 10 Values i need for this program and other values in other programs which partly the same values, using the same function and config file

and this is the config.rs where the code for the open and read of the config file is in:

use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;

// Eine Funktion, um den Wert eines Schlüssels aus einer Konfigurationsdatei zu extrahieren
pub fn get_config_value(filename: &str, section: &str, key: &str) -> Option<String> {
    // Öffnen Sie die Konfigurationsdatei
    let file = match File::open(filename) {
        Ok(file) => file,
        Err(_) => return None, // Datei konnte nicht geöffnet werden
    };
    let reader = io::BufReader::new(file);

    // Variable, um den Wert des Schlüssels zu speichern
    let mut value: Option<String> = None;

    // Durchlaufen Sie jede Zeile der Datei
    let mut in_section = false;
    for line in reader.lines() {
        let line = match line {
            Ok(line) => line,
            Err(_) => continue, // Fehler beim Lesen der Zeile überspringen
        };
        // Überprüfen, ob die Zeile mit dem gewünschten Abschnitt beginnt
        if line.starts_with(&format!("[{}]", section)) {
            in_section = true;
            continue;
        }
        // Überprüfen, ob sich die Zeile innerhalb des Abschnitts befindet
        if in_section {
            // Überprüfen, ob die Zeile den gesuchten Schlüssel enthält
            if let Some(pos) = line.find('=') {
                let (key_from_file, val) = line.split_at(pos);
                if key_from_file.trim() == key {
                    value = Some(val[1..].trim().to_string());
                    break; // Wert gefunden, Schleife beenden
                }
            }
        }
    }

    value
}

That works in the first step. I ask myself if it makes sense to put all functions in separate files. My project has 5 main programs which all use similar stuff like SQL Server connections, MQTT Message Handling (in and out), config reading, and approx. 20 function like "check user rights", get specified data of the SQL Server and so on.

Is it better to do one library and use that for all programs or put functions like MQTT function, where i have probably 5 different, User functions and so on, in different files?

It guess it makes sense to store the remaining program-relevant methods and functions in a library for each program or the program itself.

Or, what would you suggest?

The error you got is what would happen if you ran rustc src/main.rs instead of cargo build. Calling rustc directly means that your Cargo.toml was completely ignored (because Cargo's job is to read that file and pass lots of options based on it to rustc) and that you're using an old language edition (hence the mention of extern crate, which is no longer necessary today).

You can create as many module files as you want to organize your code. Most Rust programmers would probably feel that one file per function is too many, though.

“A library” is a very different thing from “a file”. If you're going to build 5 separate executables, and there is some code they all use, then it does make sense to define a library crate. Here is the file layout you would use for that, based on Cargo default project layout:

Cargo.toml
src/
    lib.rs             # root of your library; can have modules like `mod some_module;`
    some_module.rs     # a file that is part of the library
    bin/
        prog1.rs       # implicitly defines an executable named prog1
        prog2.rs       # you can `cargo run --bin prog2`
        prog3/         # if one of the programs has modules, do it like this
            main.rs    
            some_module.rs

Other than the files in this example named some_module.rs, these files are all automatically detected by Cargo. Each of the binary targets defined by having a file or directory in bin/ is automatically dependent on the library target. ("Target" here means "a unit of Rust code you can ask Cargo to build to produce an executable or a library".)

However, it can be confusing to get this set up properly and sort out any further errors, so I would recommend keeping things simpler while you're still troubleshooting — just write one program, not 5. For now.

3 Likes

Thank you for the comprehensive answer, which helps me a lot.

In any case, I will only rewrite the first of the 5 programs, that makes sense, and I also chose the simplest one, even though it is the one that works best at the moment and where Python performance is the smallest problem.

But this program does a lot of things that everyone else does, for example sending and receiving MQTT messages to each other. Access a SQL server with similar queries, write log files, check user rights and so on.

That's why I want to write all of these functions directly so, that they can also be used in the other programs.

I think I'll organize it in such a way that, for example, I put everything that has to do with MQTT into one file and can then use it in the other programs. Then it remains clear in terms of the amount of functions in a file and also in terms of the number of files itself.

I'm very excited to see how memory management and the much-vaunted security and speed of Rust will impact my project.

And I'm already looking forward to replacing my most difficult problem child, a job server written in Java, which is simply a memory waste and is a horror to maintain, due to the (for me) hard-to-read Java code.

I'm not sure what you mean by "directly", but here is a general principle: any code that is shared between several programs should be in a library crate, that is, in src/lib.rs or a module defined by it.

My previous recommendation was to avoid creating a library crate, because it can be confusing to work on a multi-crate project. However, when and if you do make multiple programs, then you should refactor to have a library crate; if you don't, and you just share source files between multiple binary crates, you will likely have problems.

Don't worry too much about choosing the right modules to create for now; it is always possible to reorganize your code. Don't overcomplicate your structure now, while you are still unfamiliar with how to organize Rust projects.

3 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.