user1244932
user1244932

Reputation: 8112

Is there a shorter way than a match or if let to get data through many nested levels without increasing the total amount of code?

I work with a bunch of structs / enums included in each other. I need to get ty.node<TyKind::Path>.1.segments.last().identifiers and ty.node<TyKind::Path>.1.segments.last().parameters<AngleBracketed::AngleBracketed>.types.

Is there a simpler way to get these two values then my implementation of f? My ideal syntax would be:

ty.node<TyKind::Path>?.1.segments.last().identifiers
// and 
ty.node<TyKind::Path>?.1.segments.last().parameters<AngleBracketed::AngleBracketed>?.types

It that's impossible, maybe there is a way to reduce the number of if let? I want to solve only this particular case, so simplification should be possible compared to f. If an analog of Option::map / Option::unwrap_or_else were introduced, then the sum of its code + the code in f should be less then my original f.

#[derive(Clone)]
struct Ty {
    node: TyKind,
}

#[derive(Clone)]
enum TyKind {
    Path(Option<i32>, Path),
}

#[derive(Clone)]
struct Path {
    segments: Vec<PathSegment>,
}

#[derive(Clone)]
struct PathSegment {
    identifier: String,
    parameters: Option<Box<PathParameters>>,
}

#[derive(Clone)]
enum PathParameters {
    AngleBracketed(AngleBracketedParameterData),
}

#[derive(Clone)]
struct AngleBracketedParameterData {
    types: Vec<Box<Ty>>,
}

/// If Tylnode == Path ->  return last path segment + types
fn f(ty: &Ty) -> Option<(String, Vec<Box<Ty>>)> {
    match ty.node {
        TyKind::Path(_, ref path) => if let Some(seg) = path.segments.iter().last() {
            let ident = seg.identifier.clone();
            println!("next_ty: seg.id {:?}", seg.identifier);
            match seg.parameters.as_ref() {
                Some(params) => match **params {
                    PathParameters::AngleBracketed(ref params) => {
                        Some((ident, params.types.clone()))
                    }
                    _ => Some((ident, vec![])),
                },
                None => Some((ident, vec![])),
            }
        } else {
            None
        },
        _ => None,
    }
}

To simplify the question, I have removed unrelated enum variants and struct fields.

Upvotes: 2

Views: 390

Answers (2)

red75prime
red75prime

Reputation: 3861

No.

The closest you can get, using nightly features and helper code, is probably this

fn f(ty: &Ty) -> MyOption<(String, Vec<Box<Ty>>)> {
    let last = ty.node.path()?.segments.my_last()?;
    Just((
        last.identifier.clone(),
        last.ab_parameters()
            .map(|v| v.types.clone())
            .unwrap_or_else(|| vec![]),
    ))
}

Playground

Upvotes: 2

user933161
user933161

Reputation:

I guess what you want is called Lenses. Not sure about Rust, but here is about Haskell https://en.m.wikibooks.org/wiki/Haskell/Lenses_and_functional_references

It might be possible to implement that in Rust, if somebody haven't done yet.

Upvotes: 0

Related Questions