Underlying function of field accesses

I've been wondering for a while if Rust has a more elegant way of index structs or tuples when mapping an Option.

With let foo = Some((x, y)) I would currently write foo.map(|foo| foo.0), but I would much prefer something like in maybe_a_string.map(String::len).

Is there any stable or proposed feature to do something like that?

I don't really get what you are asking; String is not a tuple, and its fields are all private. If you want to get the length of an Option<String> point-free style, then you can write maybe_a_string.as_ref().map(String::len)

Ah, I see the confusion I caused here. I included that specific example instead of specifying that I would like to get rid of the |foo| foo., just like I write .map(String::len) instead of map(|foo| foo.len()).

That's not possible in Rust.

Unlike languages like Python, JavaScript, or C# where the foo.some_field syntax can implicitly call a method to retrieve the field (often called a "property"), accessing a field in Rust is literally just some pointer arithmetic and (if accessing by value) a read through that pointer.

If you want to avoid writing |foo| foo.0, you could use pattern matching to get something like this |(a, _)| a.

Otherwise, if you really want to map tuples with a function, you'll need to write it yourself. This can be done using some const generics wizardry.

First, we introduce a TupleAccess trait which acts as a fancy getter to read a field by index.

trait TupleAccess<const N: usize> {
    type Value;

    fn get(self) -> Self::Value;
}

Next, we can implement it for tuples of different arities (normally I'd use a macro_rules macro here).

impl<A, B> TupleAccess<0> for (A, B) {
    type Value = A;

    fn get(self) -> A {
        self.0
    }
}

impl<A, B> TupleAccess<1> for (A, B) {
    type Value = B;

    fn get(self) -> B {
        self.1
    }
}

And finally, we can create functions that give our getters useful names.

fn first<T>(tuple: T) -> T::Value
where
    T: TupleAccess<0>,
{
    tuple.get()
}

fn second<T>(tuple: T) -> T::Value
where
    T: TupleAccess<1>,
{
    tuple.get()
}

In the end, this lets us access the tuple field with foo.map(first) syntax.

fn main() {
    let foo = Some((1, "Hello, World!"));

    println!("First: {:?}", foo.map(first));
    println!("Second: {:?}", foo.map(second));
}

(playground)

While it's a fun thought experiment, I wouldn't use this TupleAccess trick in the real world.

It's just unnecessary cognitive load you are imposing on whoever reads the code, introducing more advanced concepts like traits with const generics and helper functions that you'll always need to remember, when previously people could just read a field access and continue with their day.

5 Likes

In that case, the answer is "no". There's no built-in method on tuples to access elements without indexing them. You could come up with your own extension trait, though.

3 Likes