My initial problem was to convert a tuple of different types to a string. In Python, this would be something like:
>> a = ( 1.3, 1, 'c' )
>> b = map( lambda x: str(x), a )
['1.3', '1', 'c']
>> " ".join(b)
'1.3 1 c"
Yet, Rust doesn't support map on tuples -- only on vector-like structures. Obviously, this is due to being able to pack different types into a tuple and the lack of function overloading. Also, I couldn't find a way to get the tuple length at runtime. So, I guess, a macro would be needed to do the conversion.
As a start, I tried to match the head of an tuple, something like:
// doesn't work
match some_tuple {
(a, ..) => println!("{}", a),
_ => ()
}
So, my question:
- Is it possible, using library functions, to convert a tuple to a string, specifying an arbitrary separator?
- How to write a macro to be able to map functions to arbitrary sized tuples?
Here's an overly-clever macro solution:
The basic idea is that we can take a tuple and unpack it into a
&[&fmt::Display]
. Once we have that, it's straight-forward to map each item into a string and then combine them all with a separator. Here's what that would look like on its own:The next step would be to create a trait and implement it for the specific case:
This only implements our trait for a specific size of tuple, which may be fine for certain cases, but certainly isn't cool yet. The standard library uses some macros to reduce the drudgery of the copy-and-paste that you would need to do to get more sizes. I decided to be even lazier and reduce the copy-and-paste of that solution!
Instead of clearly and explicitly listing out each size of tuple and the corresponding index/generic name, I made my macro recursive. That way, I only have to list it out once, and all the smaller sizes are just part of the recursive call. Unfortunately, I couldn't figure out how to make it go in a forwards direction, so I just flipped everything around and went backwards. This means there's a small inefficiency in that we have to use a reverse iterator, but that should overall be a small price to pay.
The other answer helped me a lot because it clearly illustrated the power of Rust's simple macro system once you make use of recursion and pattern matching.
I've managed to make a few crude improvements (might be able to make the patterns a bit simpler, but it's rather tricky) on top of it so that the tuple accessor->type list is reversed by the macro at compile time before expansion into the trait implementation so that we no longer need to have a
.rev()
call at runtime, thus making it more efficient: