1

I'm new to Rust, probably missing something obvious. I have the following code with the main idea of being able to index any struct field like so struct_instance['field'].

use std::ops::Index;

enum Selection {
    Full,
    Partial,
}

struct Config {
    display: bool,
    timeout: u16,
    selection: Selection,
}

impl Index<&'_ str> for Config {
    type Output = bool;

    fn index(&self, index: &'_ str) -> &Self::Output {
        match index {
            "display" => &self.display,
            _ => panic!("Unknown field: {}", index),
        }
    }
}

fn main() {
    let config = Config {
        display: true,
        timeout: 500,
        selection: Selection::Partial,
    };

    let display = config["display"];

    println!("{display}");
}

The problem is: I can not find a way to index every type of struct fields, because associated type Output doesn't let me define more than one type. I would want to have match being able to process all Config fields somehow, is there a way to do so?

4
  • Why do you need to do this instead of using config.display? Commented Sep 10, 2022 at 21:40
  • @apilat very specific case where I can use one &str for both writing to file and accessing struct value. Commented Sep 10, 2022 at 21:43
  • 2
    The Index trait is mainly intended for arrays-like structures and doesn't really apply here. I would suggest not trying to use it and matching the string directly. It's difficult to give a more detailed answer without seeing code, so please include a reproducible example. Commented Sep 10, 2022 at 21:49
  • 2
    Rust is strong-typed language. Function returns a compile-time type. For returning "different" types you have to use dynamic dispatching (polymorphism). For instance, returning a Box<dyn Field> where Field must be implemented for each field of your struct. Not really useful I think tho. (Or a enum with all possible types) Commented Sep 10, 2022 at 22:03

2 Answers 2

1

As answered apilat , Index is for array like structures.

However if you want, you can achieve this with enums.

  1. Create enum with all available types of config fields (bool, u16, Selection, etc...)
  2. Change Config fields' types to this new enum
  3. Change the Output in the Index impl again to this new enum

Here is full code example

use std::ops::Index;

#[derive(Debug)]
enum ConfigField {
    Display(bool),
    Timeout(u16),
    Selection(Selection)
}

#[derive(Debug)]
enum Selection {
    Full,
    Partial,
}

struct Config {
    display: ConfigField,
    timeout: ConfigField,
    selection: ConfigField,
}

impl Index<&'_ str> for Config {
    type Output = ConfigField;

    fn index(&self, index: &'_ str) -> &Self::Output {
        match index {
            "display" => &self.display,
            "timeout" => &self.timeout,
            "selection" => &self.selection,
            _ => panic!("Unknown field: {}", index),
        }
    }
}

fn main() {
    let config = Config {
        display: ConfigField::Display(true),
        timeout: ConfigField::Timeout(500),
        selection: ConfigField::Selection(Selection::Partial),
    };

    let display = &config["display"];
    let timeout = &config["timeout"];
    let selection = &config["selection"];

    println!("{:?} {:?} {:?}", display, timeout, selection);
}
Sign up to request clarification or add additional context in comments.

Comments

1

Building an an actual Index impl around this is hard. It can be done through a side effect to the index argument:

use miniconf::{JsonCoreSlash, Miniconf};
use serde::{Deserialize, Serialize};

#[derive(Deserialize, Serialize)]
enum Selection {
    Full,
    Partial,
}

#[derive(Miniconf)]
struct Config {
    display: bool,
    timeout: u16,
    selection: Selection,
}

impl core::ops::Index<(&str, &mut &mut [u8])> for Config {
    type Output = ();
    fn index(&self, (path, buf): (&str, &mut &mut [u8])) -> &Self::Output {
        let len = self.get_json(path, *buf).unwrap();
        *buf = &mut core::mem::take(buf)[..len];
        &()
    }
}

fn main() {
    let config = Config {
        display: true,
        timeout: 500,
        selection: Selection::Partial,
    };

    let mut buf = [0; 64];
    let mut slic = &mut buf[..];

    config[("/display", &mut slic)];

    println!("{}", core::str::from_utf8(slic).unwrap());
}

1 Comment

While this uses indexing notation, this doesn't fulfill people's expectations for indexing. So this is way worse than a method.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.