How to replace const [&[&str]] with struct?

Hi there! I have this working code:

const CMDS: &'static [&[&str]] = &[
    &[",zone=nvidia xps15manjaro.temperature=", "nvidia-smi","--query-gpu=temperature.gpu","--format=csv,noheader"],
];

#[tokio::main]
async fn main() -> io::Result<()> {
    let (tx, rx) = mpsc::channel::<String>(1024);

    let mut handles = vec![];
    handles.push(tokio::spawn(data_sender(rx)));

    for def in CMDS {
        let mut cmd = Command::new(def[1]);
        for (i,arg) in def.iter().enumerate() {
            if i > 1 {
                cmd.arg(arg);
            }
        }
        println!("{:?}", cmd);
        handles.push(tokio::spawn(metrics_sender(tx.clone(), String::from(def[0]), &[], move || {
            let result = cmd.output().unwrap();
            String::from(str::from_utf8(&result.stdout).unwrap().trim())
        })));
    }

    futures::future::join_all(handles).await;
    Ok(())
}

(I've tried to remove all the code irrelevant to the question to make it easier to grasp.)

Now since I consider it ugly and bad practice to assign def[0] and def[1] the special meanings they have, I'd prefer to use a struct, but my attempt to do so:

struct ExternalSource<'a> {
    prefix: &'a str,
    cmd: &'a str,
    args: [&'a str],
}

const EXTERNAL_SOURCES: &'static [&ExternalSource<'static>] = &[&ExternalSource{
    prefix: ",zone=nvidia xps15manjaro.temperature=",
    cmd: "nvidia-smi",
    args: ["--query-gpu=temperature.gpu","--format=csv,noheader"],
}];

fails with this compiler errors:

error[E0308]: mismatched types
  --> src/main.rs:33:11
   |
33 |     args: ["--query-gpu=temperature.gpu","--format=csv,noheader"],
   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected slice `[&str]`, found array `[&str; 2]`

error[E0277]: the size for values of type `[&str]` cannot be known at compilation time
  --> src/main.rs:30:66
   |
30 |   const EXTERNAL_SOURCES: &'static [&ExternalSource<'static>] = &[&ExternalSource{
   |  __________________________________________________________________^
31 | |     prefix: ",zone=nvidia xps15manjaro.temperature=",
32 | |     cmd: "nvidia-smi",
33 | |     args: ["--query-gpu=temperature.gpu","--format=csv,noheader"],
34 | | }];
   | |_^ doesn't have a size known at compile-time
   |
   = help: within `ExternalSource<'_>`, the trait `Sized` is not implemented for `[&str]`
note: required because it appears within the type `ExternalSource<'_>`
  --> src/main.rs:24:8
   |
24 | struct ExternalSource<'a> {
   |        ^^^^^^^^^^^^^^
   = note: structs must have a statically known size to be initialized

Some errors have detailed explanations: E0277, E0308.
For more information about an error, try `rustc --explain E0277`.

Ah! I've done it!

struct ExternalSource<'a> {
    prefix: &'a str,
    cmd: &'a str,
    args: &'a[&'a str],
}

const EXTERNAL_SOURCES: &'static [&ExternalSource<'static>] = &[&ExternalSource{
    prefix: ",zone=nvidia xps15manjaro.temperature=",
    cmd: "nvidia-smi",
    args: &["--query-gpu=temperature.gpu","--format=csv,noheader"],
}];

That's actually a pretty bad solution.

It ties lifetimes of two totally unrealted things together.

If you plan to only use your struct with 'static data then it's better to just specify 'static everywehere (and there would be no need to specify it in your const), if you plan to use it with dynamic data then it's usually better to have four separate lifetimes.

Note that tying together lifetimes like that only has any negative consequences if the references are going to be copied out of the struct and used independently, or if some of the references are mutable.

If the struct is only ever used as a whole, then there is no need to complicate things with multiple lifetime parameters.

could you show a concrete code snippet how your suggestion would work, please?

Don't specify variable lifetimes for elements of that structure:

struct ExternalSource {
    prefix: &'static str,
    cmd: &'static str,
    args: &'static [&'static str],
}

const EXTERNAL_SOURCES: &'static [&ExternalSource] = &[&ExternalSource{
    prefix: ",zone=nvidia xps15manjaro.temperature=",
    cmd: "nvidia-smi",
    args: &["--query-gpu=temperature.gpu","--format=csv,noheader"],
}];

Yes, it means you can not change it in runtime, but if you do plan to work with it and change it then you need to think where all these elements would come from, who would own what and when, you can not just add lifetime annotation and hope that everything would start to magically work.

2 Likes