A simple formula interpreter - rust

To understand Rust, I am trying to implement a little formula interpreter.
An expression can only be an integer, a sum, a variable (Term) or an assignment (Set). We can then evaluate an expression. Since symbols with no associated values can appear in an expression, its evaluation yields another expression (and not necessarily an integer).
The values of the variables (if there are any) can be found in a hash table.
use std::rc::Rc;
use std::collections::HashMap;
enum Expr {
Integer(i32),
Term(String),
Plus(Rc<Expr>, Rc<Expr>),
Set(Rc<Expr>, Rc<Expr>),
}
impl Expr {
fn evaluate(&self, env: &mut HashMap<String, Expr>) -> Expr {
match *self {
Expr::Plus(ref a, ref b) => {
let a_ev = Rc::new(a.evaluate(env));
let b_ev = Rc::new(b.evaluate(env));
match (*a_ev, *b_ev) {
(Expr::Integer(x), Expr::Integer(y)) => Expr::Integer(x + y),
_ => Expr::Plus(a_ev, b_ev),
}
}
Expr::Term(ref a) => *env.get(&a).unwrap(),
Expr::Set(ref a, ref b) => {
let b_ev = Rc::new(b.evaluate(env));
match **a {
Expr::Term(x) => {
let x_value = env.get_mut(&x).unwrap();
*x_value = *b_ev;
*b_ev
}
otherwise => {
let a_ev = Rc::new(a.evaluate(env));
Expr::Set(a_ev, b_ev)
}
}
}
otherwise => otherwise,
}
}
}
The above code does not compile. Each match seems to borrow a variable. Moreover, I think we should not use the String type, but I can't understand why.
The compilation error:
error[E0277]: the trait bound `std::string::String: std::borrow::Borrow<&std::string::String>` is not satisfied
--> src/main.rs:22:39
|
22 | Expr::Term(ref a) => *env.get(&a).unwrap(),
| ^^^ the trait `std::borrow::Borrow<&std::string::String>` is not implemented for `std::string::String`
|
= help: the following implementations were found:
<std::string::String as std::borrow::Borrow<str>>

This question is somewhat subjective, but here are some problems I see:
let a_ev = Rc::new(a.evaluate(env));
let b_ev = Rc::new(b.evaluate(env));
match (*a_ev, *b_ev) {
(Expr::Integer(x), Expr::Integer(y)) => Expr::Integer(x+y),
_ => Expr::Plus(a_ev,b_ev)
}
Here, you can't dereference a_ev and b_ev because *a_ev is owned by the Rc container holding it. You can fix this error by waiting until you actually need the values to be put in Rc containers to create them:
match (a.evaluate(env), b.evaluate(env)) {
(Expr::Integer(x), Expr::Integer(y)) => Expr::Integer(x + y),
(a_ev, b_ev) => Expr::Plus(Rc::new(a_ev), Rc::new(b_ev))
}
Expr::Term(ref a) => *env.get(&a).unwrap()
Here, the variable a has type &String, and so writing &a makes no sense -- it would be like a reference to a reference. That can be fixed by changing &a to a. Also, env.get(a).unwrap() is a reference to a Expr that is owned by the HashMap, so you can't dereference/move it. One solution to this problem would be to use a HashMap<String, Rc<Expr>> instead of a HashMap<String, Expr>. Another would be to simply clone the value:
Expr::Term(ref a) => env.get(a).unwrap().clone(),
In order to be able to clone the value, you must use a "derive" compiler directive to say that Expr implements that trait:
#[derive(Clone)]
enum Expr { // ...
let b_ev = Rc::new(b.evaluate(env));
match **a {
Expr::Term(x) => {
let x_value = env.get_mut(&x).unwrap();
*x_value = *b_ev;
*b_ev
},
// ...
Here, you move *b_ev into the HashMap and then try to dereference/move it again by returning it. Also, like above, you have an extra &. Both of these issues can be solved in the same way as above:
let b_ev = b.evaluate(env);
match **a {
Expr::Term(ref x) => {
let x_value = env.get_mut(x).unwrap();
*x_value = b_ev.clone();
b_ev
},
// ...
otherwise => { let a_ev = Rc::new(a.evaluate(env)); // ...
Here, you are moving **a into otherwise while it is still owned by an Rc container. Since you don't use otherwise, the problem is easily fixed by replacing it with _:
_ => { // ...
otherwise => otherwise
You can't take a value that is owned by something else (*self is owned by something else) and return it by value. You can, however, clone it:
_ => self.clone()
Overall, the problem with your code is that it tries to duplicate data in a few places. As I said above, there are two ways of fixing it that I can think of: using Rc<Expr> everywhere instead of Expr, or using clone. Here is a fixed version of your code that compiles and uses clone:
use std::rc::Rc;
use std::collections::HashMap;
#[derive(Clone, Debug)]
enum Expr {
Integer(i32),
Term(String),
Plus(Rc<Expr>, Rc<Expr>),
Set(Rc<Expr>, Rc<Expr>),
}
impl Expr {
fn evaluate(&self, env: &mut HashMap<String, Expr>) -> Expr {
match *self {
Expr::Plus(ref a, ref b) => {
match (a.evaluate(env), b.evaluate(env)) {
(Expr::Integer(x), Expr::Integer(y)) => Expr::Integer(x + y),
(a_ev, b_ev) => Expr::Plus(Rc::new(a_ev), Rc::new(b_ev))
}
},
Expr::Term(ref a) => env.get(a).unwrap().clone(),
Expr::Set(ref a, ref b) => {
let b_ev = b.evaluate(env);
match **a {
Expr::Term(ref x) => {
let x_value = env.get_mut(x).unwrap();
*x_value = b_ev.clone();
b_ev
},
_ => {
let a_ev = a.evaluate(env);
Expr::Set(Rc::new(a_ev), Rc::new(b_ev))
}
}
}
_ => self.clone()
}
}
}
fn main() {
let e = Expr::Plus(Rc::new(Expr::Integer(9)), Rc::new(Expr::Integer(34)));
let mut env = HashMap::new();
println!("{:?}", e.evaluate(&mut env));
}
[playpen]

This is a second version where I follow Adrian's suggestion to replace all ExprNode with Rc<ExprNode>. The only cloned variables are Rc pointers, so I guess this just increments a reference count. My only regret is that we lost the method syntax, but I think this can be repaired by defining Expr with a struct instead of a type alias.
use std::rc::Rc;
use std::collections::HashMap;
#[derive(Debug)]
enum ExprNode {
Integer(i32),
Term(String),
Plus(Expr, Expr),
Set(Expr, Expr),
}
type Expr = Rc<ExprNode>;
type Env = HashMap<String, Expr>;
fn evaluate(e: &Expr, env: &mut Env) -> Expr {
match **e {
ExprNode::Plus(ref a, ref b) => {
let a_ev = evaluate(a, env);
let b_ev = evaluate(b, env);
match (&*a_ev, &*b_ev) {
(&ExprNode::Integer(x), &ExprNode::Integer(y)) => Rc::new(ExprNode::Integer(x + y)),
_ => Rc::new(ExprNode::Plus(a_ev, b_ev)),
}
}
ExprNode::Term(ref a) => env.get(a).unwrap().clone(),
ExprNode::Set(ref a, ref b) => {
let b_ev = evaluate(b, env);
match **a {
ExprNode::Term(ref x) => {
let x_value = env.get_mut(x).unwrap();
*x_value = b_ev;
x_value.clone()
}
_ => Rc::new(ExprNode::Set(evaluate(a, env), b_ev)),
}
}
_ => e.clone(),
}
}
fn main() {
let e = Rc::new(ExprNode::Plus(
Rc::new(ExprNode::Integer(9)),
Rc::new(ExprNode::Integer(4)),
));
let mut env = HashMap::new();
println!("{:?}", evaluate(&e, &mut env));
}

Related

How do I append to a tuple?

I have something which I know is a tuple. How do I generically append a single value to the tuple?
let some_tuple: (i32, &str, bool) = (1, "Hello", true);
let with_world: (i32, &str, bool, &str) = some_tuple.append("World");
As a possible use-case, consider a parser combinator which takes a variadic number of parsers and runs each in sequence, producing a tuple of their attributes:
// Emulate variadic parameters:
macro_rules! seq {
($($parsers:expr),*) => {
move |input: &[u8]| {
let result = ();
let remaining = input;
$(
let (remaining, parser_result) = $parsers(remaining);
let result = result.append(parser_result);
)*
result
}
}
}
With tuple appending, seq!(a, b, c, d) can yield (A, B, C, D), rather than the ugly (((A, B), C), D).
As an alternative to a tuple, consider using an HList:
use frunk::hlist; // 0.3.0
fn main() {
let some_values = hlist![1, "Hello", true];
let with_world = some_values.prepend("World");
println!("{:?}", with_world);
}
There's no built-in way to do this, but we can implement it ourselves with a helper trait and a macro to implement that trait for tuples up to N arguments:
trait TupleAppend<T> {
type ResultType;
fn append(self, t: T) -> Self::ResultType;
}
impl<T> TupleAppend<T> for () {
type ResultType = (T,);
fn append(self, t: T) -> Self::ResultType {
(t,)
}
}
macro_rules! impl_tuple_append {
( () ) => {};
( ( $t0:ident $(, $types:ident)* ) ) => {
impl<$t0, $($types,)* T> TupleAppend<T> for ($t0, $($types,)*) {
// Trailing comma, just to be extra sure we are dealing
// with a tuple and not a parenthesized type/expr.
type ResultType = ($t0, $($types,)* T,);
fn append(self, t: T) -> Self::ResultType {
// Reuse the type identifiers to destructure ourselves:
let ($t0, $($types,)*) = self;
// Create a new tuple with the original elements, plus the new one:
($t0, $($types,)* t,)
}
}
// Recurse for one smaller size:
impl_tuple_append! { ($($types),*) }
};
}
impl_tuple_append! {
// Supports tuples up to size 10:
(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10)
}
Playground

How can I get the T from an Option<T> when using syn?

I'm using syn to parse Rust code. When I read a named field's type using field.ty, I get a syn::Type. When I print it using quote!{#ty}.to_string() I get "Option<String>".
How can I get just "String"? I want to use #ty in quote! to print "String" instead of "Option<String>".
I want to generate code like:
impl Foo {
pub set_bar(&mut self, v: String) {
self.bar = Some(v);
}
}
starting from
struct Foo {
bar: Option<String>
}
My attempt:
let ast: DeriveInput = parse_macro_input!(input as DeriveInput);
let data: Data = ast.data;
match data {
Data::Struct(ref data) => match data.fields {
Fields::Named(ref fields) => {
fields.named.iter().for_each(|field| {
let name = &field.ident.clone().unwrap();
let ty = &field.ty;
quote!{
impl Foo {
pub set_bar(&mut self, v: #ty) {
self.bar = Some(v);
}
}
};
});
}
_ => {}
},
_ => panic!("You can derive it only from struct"),
}
My updated version of the response from #Boiethios, tested and used in a public crate, with support of several syntaxes for Option:
Option
std::option::Option
::std::option::Option
core::option::Option
::core::option::Option
fn extract_type_from_option(ty: &syn::Type) -> Option<&syn::Type> {
use syn::{GenericArgument, Path, PathArguments, PathSegment};
fn extract_type_path(ty: &syn::Type) -> Option<&Path> {
match *ty {
syn::Type::Path(ref typepath) if typepath.qself.is_none() => Some(&typepath.path),
_ => None,
}
}
// TODO store (with lazy static) the vec of string
// TODO maybe optimization, reverse the order of segments
fn extract_option_segment(path: &Path) -> Option<&PathSegment> {
let idents_of_path = path
.segments
.iter()
.into_iter()
.fold(String::new(), |mut acc, v| {
acc.push_str(&v.ident.to_string());
acc.push('|');
acc
});
vec!["Option|", "std|option|Option|", "core|option|Option|"]
.into_iter()
.find(|s| &idents_of_path == *s)
.and_then(|_| path.segments.last())
}
extract_type_path(ty)
.and_then(|path| extract_option_segment(path))
.and_then(|path_seg| {
let type_params = &path_seg.arguments;
// It should have only on angle-bracketed param ("<String>"):
match *type_params {
PathArguments::AngleBracketed(ref params) => params.args.first(),
_ => None,
}
})
.and_then(|generic_arg| match *generic_arg {
GenericArgument::Type(ref ty) => Some(ty),
_ => None,
})
}
You should do something like this untested example:
use syn::{GenericArgument, PathArguments, Type};
fn extract_type_from_option(ty: &Type) -> Type {
fn path_is_option(path: &Path) -> bool {
leading_colon.is_none()
&& path.segments.len() == 1
&& path.segments.iter().next().unwrap().ident == "Option"
}
match ty {
Type::Path(typepath) if typepath.qself.is_none() && path_is_option(typepath.path) => {
// Get the first segment of the path (there is only one, in fact: "Option"):
let type_params = typepath.path.segments.iter().first().unwrap().arguments;
// It should have only on angle-bracketed param ("<String>"):
let generic_arg = match type_params {
PathArguments::AngleBracketed(params) => params.args.iter().first().unwrap(),
_ => panic!("TODO: error handling"),
};
// This argument must be a type:
match generic_arg {
GenericArgument::Type(ty) => ty,
_ => panic!("TODO: error handling"),
}
}
_ => panic!("TODO: error handling"),
}
}
There's not many things to explain, it just "unrolls" the diverse components of a type:
Type -> TypePath -> Path -> PathSegment -> PathArguments -> AngleBracketedGenericArguments -> GenericArgument -> Type.
If there is an easier way to do that, I would be happy to know it.
Note that since syn is a parser, it works with tokens. You cannot know for sure that this is an Option. The user could, for example, type std::option::Option, or write type MaybeString = std::option::Option<String>;. You cannot handle those arbitrary names.

Why does matching on the result of Regex::find complain about expecting a struct regex::Match but found tuple?

I copied this code from Code Review into IntelliJ IDEA to try and play around with it. I have a homework assignment that is similar to this one (I need to write a version of Linux's bc in Rust), so I am using this code only for reference purposes.
use std::io;
extern crate regex;
#[macro_use]
extern crate lazy_static;
use regex::Regex;
fn main() {
let tokenizer = Tokenizer::new();
loop {
println!("Enter input:");
let mut input = String::new();
io::stdin()
.read_line(&mut input)
.expect("Failed to read line");
let tokens = tokenizer.tokenize(&input);
let stack = shunt(tokens);
let res = calculate(stack);
println!("{}", res);
}
}
#[derive(Debug, PartialEq)]
enum Token {
Number(i64),
Plus,
Sub,
Mul,
Div,
LeftParen,
RightParen,
}
impl Token {
/// Returns the precedence of op
fn precedence(&self) -> usize {
match *self {
Token::Plus | Token::Sub => 1,
Token::Mul | Token::Div => 2,
_ => 0,
}
}
}
struct Tokenizer {
number: Regex,
}
impl Tokenizer {
fn new() -> Tokenizer {
Tokenizer {
number: Regex::new(r"^[0-9]+").expect("Unable to create the regex"),
}
}
/// Tokenizes the input string into a Vec of Tokens.
fn tokenize(&self, mut input: &str) -> Vec<Token> {
let mut res = vec![];
loop {
input = input.trim_left();
if input.is_empty() { break }
let (token, rest) = match self.number.find(input) {
Some((_, end)) => {
let (num, rest) = input.split_at(end);
(Token::Number(num.parse().unwrap()), rest)
},
_ => {
match input.chars().next() {
Some(chr) => {
(match chr {
'+' => Token::Plus,
'-' => Token::Sub,
'*' => Token::Mul,
'/' => Token::Div,
'(' => Token::LeftParen,
')' => Token::RightParen,
_ => panic!("Unknown character!"),
}, &input[chr.len_utf8()..])
}
None => panic!("Ran out of input"),
}
}
};
res.push(token);
input = rest;
}
res
}
}
/// Transforms the tokens created by `tokenize` into RPN using the
/// [Shunting-yard algorithm](https://en.wikipedia.org/wiki/Shunting-yard_algorithm)
fn shunt(tokens: Vec<Token>) -> Vec<Token> {
let mut queue = vec![];
let mut stack: Vec<Token> = vec![];
for token in tokens {
match token {
Token::Number(_) => queue.push(token),
Token::Plus | Token::Sub | Token::Mul | Token::Div => {
while let Some(o) = stack.pop() {
if token.precedence() <= o.precedence() {
queue.push(o);
} else {
stack.push(o);
break;
}
}
stack.push(token)
},
Token::LeftParen => stack.push(token),
Token::RightParen => {
let mut found_paren = false;
while let Some(op) = stack.pop() {
match op {
Token::LeftParen => {
found_paren = true;
break;
},
_ => queue.push(op),
}
}
assert!(found_paren)
},
}
}
while let Some(op) = stack.pop() {
queue.push(op);
}
queue
}
/// Takes a Vec of Tokens converted to RPN by `shunt` and calculates the result
fn calculate(tokens: Vec<Token>) -> i64 {
let mut stack = vec![];
for token in tokens {
match token {
Token::Number(n) => stack.push(n),
Token::Plus => {
let (b, a) = (stack.pop().unwrap(), stack.pop().unwrap());
stack.push(a + b);
},
Token::Sub => {
let (b, a) = (stack.pop().unwrap(), stack.pop().unwrap());
stack.push(a - b);
},
Token::Mul => {
let (b, a) = (stack.pop().unwrap(), stack.pop().unwrap());
stack.push(a * b);
},
Token::Div => {
let (b, a) = (stack.pop().unwrap(), stack.pop().unwrap());
stack.push(a / b);
},
_ => {
// By the time the token stream gets here, all the LeftParen
// and RightParen tokens will have been removed by shunt()
unreachable!();
},
}
}
stack[0]
}
When I run it, however, it gives me this error:
error[E0308]: mismatched types
--> src\main.rs:66:22
|
66 | Some((_, end)) => {
| ^^^^^^^^ expected struct `regex::Match`, found tuple
|
= note: expected type `regex::Match<'_>`
found type `(_, _)`
It's complaining that I am using a tuple for the Some() method when I am supposed to use a token. I am not sure what to pass for the token, because it appears that the tuple is traversing through the Token options. How do I re-write this to make the Some() method recognize the tuple as a Token? I have been working on this for a day but I have not found any really good solutions.
The code you are referencing is over two years old. Notably, that predates regex 1.0. Version 0.1.80 defines Regex::find as:
fn find(&self, text: &str) -> Option<(usize, usize)>
while version 1.0.6 defines it as:
pub fn find<'t>(&self, text: &'t str) -> Option<Match<'t>>
However, Match defines methods to get the starting and ending indices the code was written assuming. In this case, since you only care about the end index, you can call Match::end:
let (token, rest) = match self.number.find(input).map(|x| x.end()) {
Some(end) => {
// ...

Implementing fmt::Display for enum - cannot move out of borrowed content [duplicate]

I have an enum:
enum Expr {
Lit(u32),
Var(Id),
Ass(Id, u32),
Add(u32, u32),
Sub(u32, u32),
Mul(u32, u32),
}
I'm trying to implement a method:
impl Expr {
fn eval(&self, env: &mut Env) -> Result<u32, String> {
use Expr::*;
match *self {
Lit(l) => Ok(l),
Var(id) => env.lookup(&id).ok_or_else(|| format!("undefined var {:?}", id)),
Ass(id, v) => {
env.assign(id, v);
Ok(v)
}
Add(f, s) => Ok(f + s),
Sub(f, s) => Ok(f - s),
Mul(f, s) => Ok(f * s),
}
}
}
but I'm getting the following error:
error[E0507]: cannot move out of borrowed content
--> src/main.rs:25:15
|
25 | match *self {
| ^^^^^ cannot move out of borrowed content
26 | Lit(l) => Ok(l),
27 | Var(id) => env.lookup(&id).ok_or_else(|| format!("undefined var {:?}", id)),
| -- hint: to prevent move, use `ref id` or `ref mut id`
28 | Ass(id, v) => {
| -- ...and here (use `ref id` or `ref mut id`)
Without the star, I'm also getting an error:
error[E0308]: mismatched types
--> src/main.rs:25:17
|
25 | Lit(l) => Ok(l),
| ^^^^^^ expected &Expr, found enum `Expr`
|
= note: expected type `&Expr`
= note: found type `Expr`
I think I understand the first error: I'm trying to do more than I'm allowed with the (immutable) borrowed self, but I'm not really sure about the second error. I have no idea how to do this properly.
For the first question, you need to use the ref keyword, as said by #Adrian:
impl Expr {
fn eval(&self, env: &mut Env) -> Result<u32, String> {
use Expr::*;
match *self {
Lit(l) => Ok(l),
Var(ref id) => env.lookup(id).ok_or_else(|| format!("undefined var {:?}", id)),
Ass(ref id, v) => {
env.assign(id.clone(), v);
Ok(v)
}
Add(f, s) => Ok(f + s),
Sub(f, s) => Ok(f - s),
Mul(f, s) => Ok(f * s),
}
}
}
Using ref prevents the pattern matching from taking ownership of id. As you mention, you are not allowed to take the value of id out of the Expr because you only have an immutable reference. v, f, and s don't have this problem because they are u32, which implements Copy. Instead of taking the value out, they are copied, leaving the original in place.
I don't know what the Env or Id type are, or the definitions of lookup and assign, so perhaps some clone() calls are not necessary.
For your second question, this is because self is of type &Expr, so you need to include & in the patterns:
impl Expr {
fn eval(&self, env: &mut Env) -> Result<u32, String> {
use Expr::*;
match self {
&Lit(l) => Ok(l),
&Var(ref id) => env.lookup(id).ok_or_else(|| format!("undefined var {:?}", id)),
&Ass(ref id, v) => {
env.assign(id.clone(), v);
Ok(v)
}
&Add(f, s) => Ok(f + s),
&Sub(f, s) => Ok(f - s),
&Mul(f, s) => Ok(f * s),
}
}
}
Both forms of matching are equivalent, but *self is more idiomatic and requires less typing :)

Is there an easy way to cast entire tuples of scalar values at once?

I want to cast a (u16, u16) to a (f32, f32). This is what I tried:
let tuple1 = (5u16, 8u16);
let tuple2 = tuple1 as (f32, f32);
Ideally, I would like to avoid writing
let tuple2 = (tuple1.0 as f32, tuple1.1 as f32);
There's no built-in way to do this, but one can do it with a macro:
macro_rules! tuple_as {
($t: expr, ($($ty: ident),*)) => {
{
let ($($ty,)*) = $t;
($($ty as $ty,)*)
}
}
}
fn main() {
let t: (u8, char, isize) = (97, 'a', -1);
let other = tuple_as!(t, (char, i32, i8));
println!("{:?}", other);
}
Prints ('a', 97, -1).
The macro only works for casting between types with names that are a single identifier (that's what the : ident refers to), since it reuses those names for binding to the elements of the source tuple to be able to cast them. All primitive types are valid single identifiers, so it works well for those.
No, you cannot. This is roughly equivalent to "can I cast all the fields in a struct to different types all at once?".
You can write a generic extension trait which can do this conversion for you, the only problem is that I don't believe there's any existing generic "conversion" trait which also has a u16 -> f32 implementation defined.
If you really want a function that does this, here is an as-minimal-as-I-could-make-it skeleton you can build on:
trait TupleCast<T> {
type Output;
fn tuple_cast(self) -> <Self as TupleCast<T>>::Output;
}
impl<T> TupleCast<T> for () {
type Output = ();
fn tuple_cast(self) -> <() as TupleCast<T>>::Output {
()
}
}
impl<S, T> TupleCast<T> for (S,) where S: CustomAs<T> {
type Output = (T,);
fn tuple_cast(self) -> <(S,) as TupleCast<T>>::Output {
(self.0.custom_as(),)
}
}
impl<S, T> TupleCast<T> for (S, S) where S: CustomAs<T> {
type Output = (T, T);
fn tuple_cast(self) -> <(S, S) as TupleCast<T>>::Output {
(self.0.custom_as(), self.1.custom_as())
}
}
// You would probably have more impls, up to some size limit.
// We can't use std::convert::From, because it isn't defined for the same
// basic types as the `as` operator is... which kinda sucks. So, we have
// to implement the desired conversions ourselves.
//
// Since this would be hideously tedious, we can use a macro to speed things
// up a little.
trait CustomAs<T> {
fn custom_as(self) -> T;
}
macro_rules! custom_as_impl {
($src:ty:) => {};
($src:ty: $dst:ty) => {
impl CustomAs<$dst> for $src {
fn custom_as(self) -> $dst {
self as $dst
}
}
};
($src:ty: $dst:ty, $($rest:ty),*) => {
custom_as_impl! { $src: $dst }
custom_as_impl! { $src: $($rest),* }
};
}
// You could obviously list others, or do manual impls.
custom_as_impl! { u16: u16, u32, u64, i32, i64, f32, f64 }
fn main() {
let x: (u16, u16) = (1, 2);
let y: (f32, f32) = x.tuple_cast();
println!("{:?}", y);
}
No,
there
is
not.
this version handles a few more cases Playground Example
original source: https://stackoverflow.com/a/29981602/5979634
because of matching rules, for single type casts just use as_tuple!(expr, T) or as_tuple!(expr, (T))
the rest works as in the original answer
macro_rules! tuple_as {
($t: expr, $ty: ident) => {{
let (a, b) = $t;
let a = a as $ty;
let b = b as $ty;
(a, b)
}};
($t: expr, ($ty: ident)) => {{
let (a, b) = $t;
let a = a as $ty;
let b = b as $ty;
(a, b)
}};
($t: expr, ($($ty: ident),*)) => {{
let ($($ty,)*) = $t;
($($ty as $ty,)*)
}}}

Resources