I have an enum which is defined as follows:
enum MyEnum {
X(u32),
Y(Vec<MyEnum>),
Z(Vec<MyEnum>),
}
As you can see, the enum is nested and can have other enums of its type inside of it. I'm trying to perform a simple check: I want to check if any of the u32s is greater than 5.
I have thought about using recursion here as this seems like an inherently recursive problem but from my searches it was clear that rust doesn't support recursion or that it is not recommended that it is used. How would I go about solving this problem and performing the check?
You can make a method over your Enum, and recursively call it:
enum MyEnum {
X(u32),
Y(Vec<MyEnum>),
Z(Vec<MyEnum>),
}
impl MyEnum {
fn greater_than(&self, x: u32) -> bool {
match self {
Self::X(v) => v > &x,
Self::Y(v) | Self::Z(v) => v.iter().any(|y| y.greater_than(x)),
}
}
}
Playground
It is perfectly ok to use recursion in Rust, there is no particular problem about it.
In your case, you could write your check like this:
fn my_check(en: &MyEnum) -> bool {
match en {
MyEnum::X(n) => *n > 5,
MyEnum::Y(v) | MyEnum::Z(v) => v.iter().any(my_check),
}
}
Related
Suppose I have the following src/lib.rs
pub enum Toppings {
Almond,
Caramel,
Cookie,
Marshmallow,
ChocolateSprinkles,
}
pub enum Icecream {
Chocolate(Toppings),
Mint(Toppings),
Vanilla(Toppings),
}
I want to allow pattern destructure s.t. people can use the library like any other enum with associated data:
use Icecream::*;
use Toppings::*;
fn order_how_many(icecream: Icecream) -> usize {
match icecream {
Chocolate(Marshmallow) => 42,
Vanilla(Cookie) => 69,
_ => 42069,
}
}
But at the same time I want to forbid creation of certain combinations of enum and its associated data, e.g. maybe the local ice cream store thinks double chocolate is too much and would never sell Icecream::Chocolate(Toppings::ChocolateSprinkles), so we should outright forbid any possibility this combination is constructed. I find this hard to implement in Rust, since I need pub enum to allow pattern destruction, but making the enum public means all of its variants are pub.
I tried private token similar to which sometimes could be found in sealed trait pattern, using private modules and prevents any accidental pub use via #[forbid(missing_docs)], s.t. only crate implementation can decide what Icecream/Toppings combination are possible, but this makes pattern matching ugly (require _ or .. in every pattern destruction).
mod icecream {
// Intentionally NOT use /// comment for this mod
// to prevent accidental `pub use`
#[forbid(missing_docs)]
mod hide {
pub struct Hide;
}
pub enum Toppings {
Almond,
Caramel,
Cookie,
Marshmallow,
ChocolateSprinkles,
}
pub enum Icecream {
Chocolate(Toppings, hide::Hide),
Mint(Toppings, hide::Hide),
Vanilla(Toppings, hide::Hide),
}
pub use Icecream::*;
pub use Toppings::*;
}
pub use icecream::*;
fn order_how_many(icecream: Icecream) -> usize { // OK
match icecream {
Chocolate(Marshmallow, _) => 42,
Vanilla(Cookie, ..) => 69,
_ => 42069,
}
}
fn str_to_icecream(base: &str, topping: &str) -> Option<Icecream> { // Compile fail as intended
if base.to_lowercase() == "chocollate" && topping.to_lowercase() == "chocolatesprinkles" {
Some(Chocolate(ChocolateSprinkles, icecream::hide::Hide))
} else {
None
}
}
Before posting the question SO suggested me this, but it also doesn't seem to fix the problem of enum here, since enum unlike struct cannot have different visibility between the type itself and its associated members, and that this would make having different shape of associated data more cumbersome to implement, e.g. if the length of tuples are different I'd probably have to implement a trait and return trait objects if I were to use this method.
Is there a more elegant/natural/rustacean way to do this? Or one should outright try avoid such code at the beginning, maybe since it's deemed as a bad practice?
You can't allow pattern-matching on enums without also allowing them to be constructed.
However, struct fields can be private, which lets you do things like this:
pub enum Toppings {
Almond,
Caramel,
Cookie,
Marshmallow,
ChocolateSprinkles,
}
pub enum Flavour {
Chocolate,
Mint,
Vanilla,
}
pub struct Icecream {
pub flavour: Flavour,
pub toppings: Toppings,
_secret: (), // non-pub, prevents construction from outside the module
}
You can no longer construct Icecream except from functions in the same module; these are the only things allowed to access the _secret field. Code outside of the module may still destructure the values, but must use .. to avoid naming the _secret field.
You'd rewrite your other functions like this:
// this can be in any module
fn order_how_many(icecream: Icecream) -> usize { // OK
match icecream {
Icecream { flavour: Chocolate, .. } => 42,
Icecream { flavour: Vanilla, toppings: Cookie, .. } => 69,
_ => 42069,
}
}
// this must be in the same module as the `Icecream` struct
fn str_to_icecream(base: &str, topping: &str) -> Option<Icecream> {
if base.to_lowercase() == "chocolate" && topping.to_lowercase() == "chocolatesprinkles" {
Some(
Icecream {
flavour: Chocolate,
toppings: ChocolateSprinkles,
_secret: ()
}
)
} else {
None
}
}
An enum is clearly a kind of key/value pair structure. Consequently, it would be nice to automatically create a dictionary from one wherein the enum variants become the possible keys and their payload the associated values. Keys without a payload would use the unit value. Here is a possible usage example:
enum PaperType {
PageSize(f32, f32),
Color(String),
Weight(f32),
IsGlossy,
}
let mut dict = make_enum_dictionary!(
PaperType,
allow_duplicates = true,
);
dict.insert(dict.PageSize, (8.5, 11.0));
dict.insert(dict.IsGlossy, ());
dict.insert_def(dict.IsGlossy);
dict.remove_all(dict.PageSize);
Significantly, since an enum is merely a list of values that may optionally carry a payload, auto-magically constructing a dictionary from it presents some semantic issues.
How does a strongly typed Dictionary<K, V> maintain the discriminant/value_type dependency inherent with enums where each discriminant has a specific payload type?
enum Ta {
K1(V1),
K2(V2),
...,
Kn(Vn),
}
How do you conveniently refer to an enum discriminant in code without its payload (Ta.K1?) and what type is it (Ta::Discriminant?) ?
Is the value to be set and get the entire enum value or just the payload?
get(&self, key: Ta::Discriminant) -> Option<Ta>
set(&mut self, value: Ta)
If it were possible to augment an existing enum auto-magically with another enum of of its variants then a reasonably efficient solution seems plausible in the following pseudo code:
type D = add_discriminant_keys!( T );
impl<D> for Vec<D> {
fn get(&self, key: D::Discriminant) -> Option<D> { todo!() }
fn set(&mut self, value: D) { todo!() }
}
I am not aware whether the macro, add_discriminant_keys!, or the construct, D::Discriminant, is even feasible. Unfortunately, I am still splashing in the shallow end of the Rust pool, despite this suggestion. However, the boldness of its macro language suggests many things are possible to those who believe.
Handling of duplicates is an implementation detail.
Enum discriminants are typically functions and therefore have a fixed pointer value (as far as I know). If such values could become constants of an associated type within the enum (like a trait) with attributes similar to what has been realized by strum::EnumDiscriminants things would look good. As it is, EnumDiscriminants seems like a sufficient interim solution.
A generic implementation over HashMap using strum_macros crate is provided based on in the rust playground; however, it is not functional there due to the inability of rust playground to load the strum crate from there. A macro derived solution would be nice.
First, like already said here, the right way to go is a struct with optional values.
However, for completeness sake, I'll show here how you can do that with a proc macro.
When you want to design a macro, especially a complicated one, the first thing to do is to plan what the emitted code will be. So, let's try to write the macro's output for the following reduced enum:
enum PaperType {
PageSize(f32, f32),
IsGlossy,
}
I will already warn you that our macro will not support brace-style enum variants, nor combining enums (your add_discriminant_keys!()). Both are possible to support, but both will complicate this already-complicated answer more. I'll refer to them shortly at the end.
First, let's design the map. It will be in a support crate. Let's call this crate denum (a name will be necessary later, when we'll refer to it from our macro):
pub struct Map<E> {
map: std::collections::HashMap<E, E>, // You can use any map implementation you want.
}
We want to store the discriminant as a key, and the enum as the value. So, we need a way to refer to the free discriminant. So, let's create a trait Enum:
pub trait Enum {
type DiscriminantsEnum: Eq + Hash; // The constraints are those of `HashMap`.
}
Now our map will look like that:
pub struct Map<E: Enum> {
map: std::collections::HashMap<E::DiscriminantsEnum, E>,
}
Our macro will generate the implementation of Enum. Hand-written, it'll be the following (note that in the macro, I wrap it in const _: () = { ... }. This is a technique used to prevent names polluting the global namespaces):
#[derive(PartialEq, Eq, Hash)]
pub enum PaperTypeDiscriminantsEnum {
PageSize,
IsGlossy,
}
impl Enum for PaperType {
type DiscriminantsEnum = PaperTypeDiscriminantsEnum;
}
Next. insert() operation:
impl<E: Enum> Map<E> {
pub fn insert(discriminant: E::DiscriminantsEnum, value: /* What's here? */) {}
}
There is no way in current Rust to refer to an enum discriminant as a distinct type. But there is a way to refer to struct as a distinct type.
We can think about the following:
pub struct PageSize;
But this pollutes the global namespace. Of course, we can call it something like PaperTypePageSize, but I much prefer something like PaperTypeDiscriminants::PageSize.
Modules to the rescue!
#[allow(non_snake_case)]
pub mod PaperTypeDiscriminants {
#[derive(Clone, Copy)]
pub struct PageSize;
#[derive(Clone, Copy)]
pub struct IsGlossy;
}
Now we need a way in insert() to validate the the provided discriminant indeed matches the wanted enum, and to refer to its value. A new trait!
pub trait EnumDiscriminant: Copy {
type Enum: Enum;
type Value;
fn to_discriminants_enum(self) -> <Self::Enum as Enum>::DiscriminantsEnum;
fn to_enum(self, value: Self::Value) -> Self::Enum;
}
And here's how our macro will implements it:
impl EnumDiscriminant for PaperTypeDiscriminants::PageSize {
type Enum = PaperType;
type Value = (f32, f32);
fn to_discriminants_enum(self) -> PaperTypeDiscriminantsEnum { PaperTypeDiscriminantsEnum::PageSize }
fn to_enum(self, (v0, v1): Self::Value) -> Self::Enum { Self::Enum::PageSize(v0, v1) }
}
impl EnumDiscriminant for PaperTypeDiscriminants::IsGlossy {
type Enum = PaperType;
type Value = ();
fn to_discriminants_enum(self) -> PaperTypeDiscriminantsEnum { PaperTypeDiscriminantsEnum::IsGlossy }
fn to_enum(self, (): Self::Value) -> Self::Enum { Self::Enum::IsGlossy }
}
And now insert():
pub fn insert<D>(&mut self, discriminant: D, value: D::Value)
where
D: EnumDiscriminant<Enum = E>,
{
self.map.insert(
discriminant.to_discriminants_enum(),
discriminant.to_enum(value),
);
}
And trivially insert_def():
pub fn insert_def<D>(&mut self, discriminant: D)
where
D: EnumDiscriminant<Enum = E, Value = ()>,
{
self.insert(discriminant, ());
}
And get() (note: seprately getting the value is possible when removing, by adding a method to the trait EnumDiscriminant with the signature fn enum_to_value(enum_: Self::Enum) -> Self::Value. It can be unsafe fn and use unreachable_unchecked() for better performance. But with get() and get_mut(), that returns reference, it's harder because you can't get a reference to the discriminant value. Here's a playground that does that nonetheless, but requires nightly):
pub fn get_entry<D>(&self, discriminant: D) -> Option<&E>
where
D: EnumDiscriminant<Enum = E>,
{
self.map.get(&discriminant.to_discriminants_enum())
}
get_mut() is very similar.
Note that my code doesn't handle duplicates but instead overwrites them, as it uses HashMap. However, you can easily create your own map that handles duplicates in another way.
Now that we have a clear picture in mind what the macro should generate, let's write it!
I decided to write it as a derive macro. You can use an attribute macro too, and even a function-like macro, but you must call it at the declaration site of your enum - because macros cannot inspect code other than the code the're applied to.
The enum will look like:
#[derive(denum::Enum)]
enum PaperType {
PageSize(f32, f32),
Color(String),
Weight(f32),
IsGlossy,
}
Usually, when my macro needs helper code, I put this code in crate and the macro in crate_macros, and reexports the macro from crate. So, the code in denum (besides the aforementioned Enum, EnumDiscriminant and Map):
pub use denum_macros::Enum;
denum_macros/src/lib.rs:
use proc_macro::TokenStream;
use quote::{format_ident, quote};
#[proc_macro_derive(Enum)]
pub fn derive_enum(item: TokenStream) -> TokenStream {
let item = syn::parse_macro_input!(item as syn::DeriveInput);
if item.generics.params.len() != 0 {
return syn::Error::new_spanned(
item.generics,
"`denum::Enum` does not work with generics currently",
)
.into_compile_error()
.into();
}
if item.generics.where_clause.is_some() {
return syn::Error::new_spanned(
item.generics.where_clause,
"`denum::Enum` does not work with `where` clauses currently",
)
.into_compile_error()
.into();
}
let (vis, name, variants) = match item {
syn::DeriveInput {
vis,
ident,
data: syn::Data::Enum(syn::DataEnum { variants, .. }),
..
} => (vis, ident, variants),
_ => {
return syn::Error::new_spanned(item, "`denum::Enum` works only with enums")
.into_compile_error()
.into()
}
};
let discriminants_mod_name = format_ident!("{}Discriminants", name);
let discriminants_enum_name = format_ident!("{}DiscriminantsEnum", name);
let mut discriminants_enum = Vec::new();
let mut discriminant_structs = Vec::new();
let mut enum_discriminant_impls = Vec::new();
for variant in variants {
let variant_name = variant.ident;
discriminant_structs.push(quote! {
#[derive(Clone, Copy)]
pub struct #variant_name;
});
let fields = match variant.fields {
syn::Fields::Named(_) => {
return syn::Error::new_spanned(
variant.fields,
"`denum::Enum` does not work with brace-style variants currently",
)
.into_compile_error()
.into()
}
syn::Fields::Unnamed(fields) => Some(fields.unnamed),
syn::Fields::Unit => None,
};
let value_destructuring = fields
.iter()
.flatten()
.enumerate()
.map(|(index, _)| format_ident!("v{}", index));
let value_destructuring = quote!((#(#value_destructuring,)*));
let value_builder = if fields.is_some() {
value_destructuring.clone()
} else {
quote!()
};
let value_type = fields.into_iter().flatten().map(|field| field.ty);
enum_discriminant_impls.push(quote! {
impl ::denum::EnumDiscriminant for #discriminants_mod_name::#variant_name {
type Enum = #name;
type Value = (#(#value_type,)*);
fn to_discriminants_enum(self) -> #discriminants_enum_name { #discriminants_enum_name::#variant_name }
fn to_enum(self, #value_destructuring: Self::Value) -> Self::Enum { Self::Enum::#variant_name #value_builder }
}
});
discriminants_enum.push(variant_name);
}
quote! {
#[allow(non_snake_case)]
#vis mod #discriminants_mod_name { #(#discriminant_structs)* }
const _: () = {
#[derive(PartialEq, Eq, Hash)]
pub enum #discriminants_enum_name { #(#discriminants_enum,)* }
impl ::denum::Enum for #name {
type DiscriminantsEnum = #discriminants_enum_name;
}
#(#enum_discriminant_impls)*
};
}
.into()
}
This macro has several flaws: it doesn't handle visibility modifiers and attributes correctly, for example. But in the general case, it works, and you can fine-tune it more.
If you want to also support brace-style variants, you can create a struct with the data (instead of the tuple we use currently).
Combining enum is possible if you'll not use a derive macro but a function-like macro, and invoke it on both enums, like:
denum::enums! {
enum A { ... }
enum B { ... }
}
Then the macro will have to combine the discriminants and use something like Either<A, B> when operating with the map.
Unfortunately, a couple of questions arise in that context:
should it be possible to use enum types only once? Or are there some which might be there multiple times?
what should happen if you insert a PageSize and there's already a PageSize in the dictionary?
All in all, a regular struct PaperType is much more suitable to properly model your domain. If you don't want to deal with Option, you can implement the Default trait to ensure that some sensible defaults are always available.
If you really, really want to go with a collection-style interface, the closest approximation would probably be a HashSet<PaperType>. You could then insert a value PaperType::PageSize.
I need to use an attribute of the generic type of a struct in a macro.
A slightly contrived but minimal example would be if I wanted to implement a method for a generic struct, that returned the minimum value of its generic type.
struct Barn<T> {
hay: T
}
macro_rules! impl_min_hay{
($barntype:ident) => {
impl $barntype {
fn min_hay(&self) -> ????T {
????T::MIN
}
}
}
}
type SmallBarn = Barn<i8>;
type BigBarn = Barn<i64>;
impl_min_hay!(SmallBarn);
impl_min_hay!(BigBarn);
fn main() {
let barn = SmallBarn { hay: 5 };
println!("{}", barn.min_hay());
}
How would I resolve from SmallBarn to get the generic type and thus it's MIN attribute?
The actual problem I am trying to solve is a change to this macro. The macro is applied to, among others, BooleanChunked, which is defined as:
pub type BooleanChunked = ChunkedArray<BooleanType>
And I need to use an attribute of BooleanType
The only general solution I can think of is to define a trait that allows you to get at the type parameter (the syntax for this is <Type as Trait>::AssociatedType):
trait HasHayType {
type HayType;
}
impl<T> HasHayType for Barn<T> {
type HayType = T;
}
macro_rules! impl_min_hay{
($barntype:ident) => {
impl $barntype {
fn min_hay(&self) -> <$barntype as HasHayType>::HayType {
<$barntype as HasHayType>::HayType::MIN
}
}
}
}
Here's the complete program on play.rust-lang.org.
That said, once you have a trait, you don't really need the macro – you can just implement min_hay on the trait (this example makes use of the widely used num-traits crate, because this approach needs a trait for "things that have minimum values"):
use num_traits::Bounded;
trait HasHayType {
type HayType: Bounded;
fn min_hay(&self) -> Self::HayType;
}
impl<T: Bounded> HasHayType for Barn<T> {
type HayType = T;
fn min_hay(&self) -> T {
T::min_value()
}
}
And here's what that looks like as a complete program.
(And of course, once you've done that too, you don't really need the separate trait either: you can inline the definition of HasHayType into Barn, using a where clause if you want to be able to handle Barns with non-numerical hay types in addition to the ones where you'd use the macro. Presumably, though, the actual situation you have is more complex than the cut-down example you used for the question, so I gave the more complex versions in case the simplified versions wouldn't work.)
As a side note, min_hay doesn't actually need the &self parameter here; you could remove it, in order to be able to learn the minimum amount of hay without needing a barn to put it in.
I have the following enum defined:
#[derive(Debug, Copy, Clone)]
struct Core;
#[derive(Debug, Copy, Clone)]
struct Mem;
#[derive(Debug, Copy, Clone)]
pub enum Atag {
Core(Core),
Mem(Mem),
Cmd(&'static str),
Unknown(u32),
None,
}
I would like to implement a function on this enum which "filters out" certain enum values. I have the following:
impl Atag {
/// Returns `Some` if this is a `Core` ATAG. Otherwise returns `None`.
pub fn core(self) -> Option<Core> {
match self {
Atag::Core => Some(self),
_ => None
}
}
}
I'm not sure why, but the compiler complains:
error[E0532]: expected unit struct/variant or constant, found tuple variant `Atag::Core`
--> src/main.rs:17:13
|
17 | Atag::Core => Some(self),
| ^^^^^^^^^^ not a unit struct/variant or constant
help: possible better candidate is found in another module, you can import it into scope
|
1 | use Core;
|
I also tried a comparison approach:
pub fn core(self) -> Option<Core> {
if self == Atag::Core {
Some(self)
} else {
None
}
}
But the compiler complains:
error[E0369]: binary operation `==` cannot be applied to type `Atag`
--> src/main.rs:20:12
|
20 | if self == Atag::Core {
| ^^^^^^^^^^^^^^^^^^
|
= note: an implementation of `std::cmp::PartialEq` might be missing for `Atag`
I think this is just a limitation of the pattern matching and is designed to prevent unexpected behavior.
The full "definition" of an Atag with type Core is Atag::Core(raw::Core). Obviously, the contents of the Core are irrelevant to you, but the compiler needs to know that everything is "accounted for" because the compiler is a stickler for the rules. The easiest way to get around this is to use the "anything pattern", _, much like you did to match non-Core variants.
impl Atag {
/// Returns `Some` if this is a `Core` ATAG. Otherwise returns `None`.
pub fn core(self) -> Option<Core> {
match self {
// The compiler now knows that a value is expected,
// but isn't necessary for the purposes of our program.
Atag::Core(_) => Some(self),
_ => None
}
}
}
To ignore multiple values, you'd use Something::Foo(_, _) - one underscore for each value in the variant, or Something::Foo(..) to ignore everything.
Remember that, unlike in some other languages, a Rust enum is not "just" a collection of different types. Data associated with an enum value is a part of it, just like the fields of a structure. So self == Atag::Core isn't a meaningful statement because it ignores the data associated with a Core. A Foo(0) is different than a Foo(12), even if they're both of the Foo variant.
I'd also like to point out if let, which is - as far as I can tell - the closest option to a standard if statement without defining a custom is_core function on Atag (which, given the existence of match and if let, is basically unnecessary).
impl Atag {
/// Returns `Some` if this is a `Core` ATAG. Otherwise returns `None`.
pub fn core(self) -> Option<Core> {
if let Atag::Core(_) = self {
Some(self)
} else {
None
}
}
}
I needed something like this to chain functions together nicely. In that case, you want to return the unwrapped core type, rather than just the enum.
I also found it easier to not consume the input, and so accepted a &self argument an returned an Option<&Core>. But you can have both.
The Rust convention has as_X as the reference-based conversion and into_X as the conversion that consumes the value. For example:
impl Atag {
fn as_core(&self) -> Option<&Core> {
if let Atag::Core(ref v) = self {
Some(v)
} else {
None
}
}
fn into_core(self) -> Option<Core> {
if let Atag::Core(v) = self {
Some(v)
} else {
None
}
}
}
fn main() {
let c = Atag::Core(Core {});
let m = Atag::Mem(Mem {});
assert_eq!(c.as_core().map(|cc| "CORE_REF"), Some("CORE_REF"));
assert_eq!(m.as_core().map(|cc| "CORE_REF"), None);
// Consume c - we cant use it after here...
assert_eq!(c.into_core().map(|cc| "NOM NOM CORE"), Some("NOM NOM CORE"));
// Consume m - we cant use it after here...
assert_eq!(m.into_core().map(|cc| "NOM NOM CORE"), None);
}
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.
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
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.