0

I am trying to create nested structs with multiple strings which will only be set once. For this I have decided to use &str. How can I write this code correctly and cleanly?

#[derive(Debug)]
struct Database<'a, 'b, 'c, 'd> {
    host: &'a str,
    port: u32,
    name: &'b str,
    user: &'c str,
    pass: &'d str, // Could be leave blank / unset
}

#[derive(Debug)]
pub struct Config<'a, 'b, 'c, 'd, 'e> {
    file: &'a str,
    database: Database<'b, 'c, 'd, 'e>,
}

impl Config<'_, '_, '_, '_, '_> {
    pub fn new(f: &str) -> Self {
        return Config {
            file: f,
            database: Database {
                host: "", // Value passed by a variable subsequently read from a configuration file
                port: 0,  // Same
                name: "", // Same
                user: "", // Same
                pass: "", // Same
            },
        };
    }
}

fn main() {
    let conf = Config::new("Ciao");
    println!("{:?}", conf);
}
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
  --> src/main.rs:18:16
   |
18 |         return Config {
   |                ^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 17:5...
  --> src/main.rs:17:5
   |
17 |     pub fn new(f: &str) -> Self {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...so that reference does not outlive borrowed content
  --> src/main.rs:19:19
   |
19 |             file: f,
   |                   ^
note: but, the lifetime must be valid for the lifetime `'_` as defined on the impl at 16:13...
  --> src/main.rs:16:13
   |
16 | impl Config<'_, '_, '_, '_, '_> {
   |             ^^
note: ...so that the expression is assignable
  --> src/main.rs:18:16
   |
18 |           return Config {
   |  ________________^
19 | |             file: f,
20 | |             database: Database {
21 | |                 host: "", // Value passed by a variable subsequently read from a configuration file
...  |
26 | |             },
27 | |         };
   | |_________^
   = note: expected `Config<'_, '_, '_, '_, '_>`
              found `Config<'_, '_, '_, '_, '_>`
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Giuseppe
  • 531
  • 1
  • 5
  • 19
  • The lifetime of `""` is `'static`, so I would explicitly state that you are returning a `Config<'l,'static,'static,'static,'static>`, which would require changing the lifetimes in `impl Config<>`. – PiRocks Oct 19 '20 at 02:51
  • 1
    I would note though that in this case you probably want to use `String` instead of `&str` – PiRocks Oct 19 '20 at 02:52
  • Sorry, I corrected the example code – Giuseppe Oct 19 '20 at 10:35
  • I understand that you should only use `String` for variables that need to be changed multiple times, but these only need to be set once for` struct`. – Giuseppe Oct 19 '20 at 10:38
  • 2
    The primary difference between `String` and `&str` is that `String` is owned, while `&str` is not. You cannot mutate the contents `&str` b/c of the reference not being mutable. It is acceptable to use String for variables you will only set once. – PiRocks Oct 19 '20 at 12:48

1 Answers1

3

You need to indicate that the string passed in is related to the field you are storing it in:

impl<'a> Config<'a, '_, '_, '_, '_> {
    pub fn new(f: &'a str) -> Self {

It's very likely that you don't want five lifetimes. See also:


Value passed by a variable subsequently read from a configuration file

This sounds like a poor design. Instead, read the config first and then provide those values up front. If you really need to have a two-step process, reflect that in your types:

struct Database;

struct Config;

struct ConfigWithDatabase {
    config: Config,
    database: Database,
}

Could be leave blank / unset

Use Option instead of sentinel values.

Giuseppe
  • 531
  • 1
  • 5
  • 19
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366