How can I prevent code redundancy in Rust - rust

I am seeking to extend the pattern by utilizing default arguments in Rust as seen in previous question by someone which is using an "arguments" struct and the From/Into traits.
However, as you can see, the code clearly looks very redundant and adding associated functions to Foo struct would require more code for the From/Into traits accordingly.
type FileName = Option<String>;
pub struct BarArg {
a: f64,
b: FileName,
}
impl Default for BarArg {
fn default() -> Self {
BarArg { a: 1.0, b: None }
}
}
impl From<()> for BarArg {
fn from(_: ()) -> Self {
Self::default()
}
}
impl From<f64> for BarArg {
fn from(a: f64) -> Self {
Self {
a,
..Self::default()
}
}
}
impl From<Option<String>> for BarArg {
fn from(b: Option<String>) -> Self {
Self {
b,
..Self::default()
}
}
}
impl From<(f64, Option<String>)> for BarArg {
fn from((a, b): (f64, Option<String>)) -> Self {
Self { a, b }
}
}
pub struct BazArg {
a: i32,
b: FileName,
}
impl Default for BazArg {
fn default() -> Self {
BazArg { a: 1, b: None }
}
}
impl From<()> for BazArg {
fn from(_: ()) -> Self {
Self::default()
}
}
impl From<i32> for BazArg {
fn from(a: i32) -> Self {
Self {
a,
..Self::default()
}
}
}
impl From<Option<String>> for BazArg {
fn from(b: Option<String>) -> Self {
Self {
b,
..Self::default()
}
}
}
impl From<(i32, Option<String>)> for BazArg {
fn from((a, b): (i32, Option<String>)) -> Self {
Self { a, b }
}
}
struct Foo;
impl Foo {
pub fn bar<A>(bar_arg: A)
where
A: Into<BarArg>,
{
match bar_arg.into().b {
Some(b) => {
println!("FileName is: {:?}", b);
}
None => {
println!("No FileName was passed");
}
};
}
pub fn baz<A>(baz_arg: A)
where
A: Into<BazArg>,
{
match baz_arg.into().b {
Some(b) => {
println!("FileName is: {:?}", b);
}
None => {
println!("No FileName was passed");
}
};
}
}
fn main() {
Foo::bar(());
Foo::bar(Some("bar.toml".to_string()));
Foo::baz(());
Foo::baz(Some("baz.toml".to_string()));
}
Playground
Is there any way to somehow implement these codes in a non-redundant and concise manner?

Don't. Do not emulate features, especially when they have good (even better) alternatives. And do not abuse other features to emulate those features.
That being said, it is possible to make it less boilerplate-y using macros. An important point to note is: what if two parameters have the same type? this will generate conflicting From implementations. You have to make sure it either doesn't happen or you only allow omitting the last parameters. You can expose the parameters struct to allow more flexible filling.
The tricky part is that both for allowing all consecutive parameter sequences and for allowing only the last we will need TT munching. This is because macro_rules! cannot extract from the middle of a list, or even from its end. It can only match its start.
Here's a macro that only allows omitting the last arguments. It also always requires you to pass a tuple, so you pass (1,) instead of just 1, for example. It is pretty easy to allow this, but I like the always-tuple approach because I feel it is more consistent (lifting the only-last limit is much harder, but possible):
macro_rules! impl_from_for_default {
// Recursion stop condition
{
$struct_name:ident {}
} => {
impl ::core::convert::From<()> for $struct_name {
fn from((): ()) -> Self {
Self::default()
}
}
};
{
$struct_name:ident {
$($param:ident : $param_ty:ty,)*
}
} => {
impl ::core::convert::From<( $($param_ty,)* )> for $struct_name {
fn from(( $($param,)* ): ( $($param_ty,)* )) -> Self {
Self {
$($param,)*
..Self::default()
}
}
}
omit_last_param! {
$struct_name
[ ]
$($param : $param_ty,)*
}
};
}
macro_rules! omit_last_param {
// Recursion stop condition
{
$struct_name:ident
[ $($first_params:tt)* ]
$last_param:ident : $last_param_ty:ty,
} => {
impl_from_for_default! {
$struct_name {
$($first_params)*
}
}
};
{
$struct_name:ident
[ $($first_params:tt)* ]
$param:ident : $param_ty:ty,
$($rest:tt)*
} => {
omit_last_param! {
$struct_name
[
$($first_params)*
$param : $param_ty,
]
$($rest)*
}
};
}
macro_rules! with_default {
{
$vis:vis $struct_name:ident {
$($param:ident : $param_ty:ty = $param_default:expr),* $(,)?
}
} => {
$vis struct $struct_name {
$($param : $param_ty,)*
}
impl ::core::default::Default for $struct_name {
fn default() -> Self {
Self {
$($param : $param_default,)*
}
}
}
impl_from_for_default! {
$struct_name {
$($param : $param_ty,)*
}
}
};
}
Playground.
Example usage:
with_default! {
pub BazArg {
a: i32 = 1,
b: FileName = None,
}
}

Related

How to reuse common codes for structs?

I want to reuse codes for structs. For example:
use std::fmt::Display;
struct CommonStruct<T: Display> {
// could have more fields
data: T
}
struct A<T: Display> {
com: CommonStruct<T>,
age: i32
}
struct B<T: Display> {
com: CommonStruct<T>,
name: String
}
impl<T: Display> A<T> {
// could be more common functions
fn print_data(&self) {
// could be more complicated
println!("data: {}", self.com.data);
}
}
impl<T: Display> B<T> {
// could be more common functions
fn print_data(&self) {
// could be more complicated
println!("data: {}", self.com.data);
}
}
fn main() {
let a = A{ com: CommonStruct{data: 10}, age: 0 };
a.print_data();
let b = B{ com: CommonStruct{data: 12}, name: "123".to_string() };
b.print_data();
}
where A and B have some common fields packed by CommonStruct and some common functions (e.g., print_data).
I tried to use trait but cannot figure out a solution:
use std::fmt::Display;
struct CommonStruct<T: Display> {
// could have more fields
data: T
}
struct A<T: Display> {
com: CommonStruct<T>,
age: i32
}
struct B<T: Display> {
com: CommonStruct<T>,
name: String
}
trait Common {
// could be more common functions
fn print_data(&self) {
print_data(&self)
}
}
impl<T: Display> Common for A<T> {
}
impl<T: Display> Common for B<T> {
}
fn print_data(t: &Common) {
// could be more complicated
println!("data: {}", t.com.data);
}
fn main() {
let a = A{ com: CommonStruct{data: 10}, age: 0 };
a.print_data();
let b = B{ com: CommonStruct{data: 12}, name: "123".to_string() };
b.print_data();
}
Since print_data only uses the CommonStruct, and A and B share no other fields, make it an implementation of CommonStruct and call it directly.
impl <T: Display> CommonStruct<T> {
fn print_data(&self) {
println!("data: {}", self.data);
}
}
fn main() {
let a = A{ com: CommonStruct{data: 10}, age: 0 };
a.com.print_data();
let b = B{ com: CommonStruct{data: 12}, name: "123".to_string() };
b.com.print_data();
}
Alternatively, make a trait which has a concrete implementation of print_data which relies on a method to get the data.
trait HasData<T: Display> {
fn get_data(&self) -> &T;
fn print_data(&self) {
// could be more complicated
println!("data: {}", self.get_data());
}
}
Then each only has to implement how to get the data.
impl<T: Display> HasData<T> for CommonStruct<T> {
fn get_data(&self) -> &T {
return &self.data;
}
}
impl<T: Display> HasData<T> for A<T> {
fn get_data(&self) -> &T {
return &self.com.data;
}
}
impl<T: Display> HasData<T> for B<T> {
fn get_data(&self) -> &T {
return &self.com.data;
}
}
fn main() {
let a = A{ com: CommonStruct{data: 1}, age: 0 };
a.print_data();
let b = B{ com: CommonStruct{data: 2}, name: "123".to_string() };
b.print_data();
let c = CommonStruct{data: 3};
c.print_data();
}
I saw that you wanted to use traits. I've included a working example below.
However, traits are used to implement shared behavior. A trait must be implemented on each type you wish to have the shared behavior. So, you do have to impl a separate print_data function for a given type.
use std::fmt::Display;
// `Common` structure of some type `T`.
struct Common<T: Display> {
data: T,
}
// `Printable` trait used to print `data`.
trait Printable {
fn print_data(&self);
}
struct A {
common: Common<String>,
}
impl A {
fn new(s: &str) -> A {
A {
common: Common { data: s.to_owned() },
}
}
}
// Implement `Printable the trait for A`.
impl Printable for A {
fn print_data(&self) {
println!("A.common.data: {}", self.common.data)
}
}
struct B {
common: Common<i32>,
}
// Implement the `Printable` trait for `B`.
impl Printable for B {
fn print_data(&self) {
println!("B.common.data: {}", self.common.data)
}
}
So that's traits, but if you must call the same function to print the data, then maybe something like the following can work for you. It defines an enum with three variants. You can then match on a particular variant as demonstrated by print_all_data.
use std::path::PathBuf;
struct G {
path: PathBuf,
common: Common<String>,
}
enum C {
D(A),
E(B),
F(G),
}
fn print_all_data(c: C) {
match c {
C::D(a) => println!("{}", a.common.data),
C::E(b) => println!("{}", b.common.data),
C::F(g) => println!("{} {:?}", g.common.data, g.path)
}
}
fn main() {
let a = A::new("hello");
a.print_data();
let b = B {
common: Common { data: 42 },
};
b.print_data();
let g = G {
path: PathBuf::from("some/path/file.txt"),
common: Common {data: "some_data".to_owned()}
};
let cfg = C::F(g);
print_all_data(cfg);
print_all_data(C::D(a));
print_all_data(C::E(b));
}

Macro that makes struct based on struct field's types

I am trying to make a macro that generates a struct with a new function implementation. The new function needs to call functions based on the type of the field and use the return value as the field value.
The new implementation should end up working like this:
struct foo {
test: i32,
other: String,
}
impl foo {
fn new() -> Self {
foo {
test: get_i32(),
other: get_string(),
}
}
}
This is the current code I have:
macro_rules! test {
(struct $name:ident { $($fname:ident : $ftype:ty),* }) => {
#[derive(Debug)]
pub struct $name {
$(pub $fname : $ftype),*
}
impl $name {
fn new(mut v: Vec<u8>) -> Self {
$name {
$($fname : ),*
}
}
}
};
}
I have tried putting a match statement but it gives incompatible arms types error.
impl $name {
fn new(mut v: Vec<u8>) -> Self {
$name {
$($fname : match &stringify!($ftype)[..] {
"i32" => get_i32(),
"String" => get_string(),
}),*
}
}
}
Thank you.
I managed to use another function that returns any to get it to work.
macro_rules! test {
(struct $name:ident { $($fname:ident : $ftype:ty),* }) => {
#[derive(Debug)]
pub struct $name {
$(pub $fname : $ftype),*
}
impl $name {
fn new(mut v: Vec<u8>) -> Self {
$name {
$($fname : get_feild::<$ftype>(stringify!($ftype)).downcast_ref::<$ftype>().unwrap().clone()),*
}
}
}
};
}
fn get_feild<T>(t: &str) -> Box<dyn std::any::Any> {
match t {
"i32" => Box::new(get_i32()),
"String" => Box::new(get_string()),
_ => panic!("UNKNOWN TYPE"),
}
}

How can I map Result<(), E> into a numeric error code?

I have a bunch of FFI functions that I call using C. The caller expects 1 for success, or -1 on failure.
struct Error;
fn my_rust_function() -> Result<(), Error> {
Ok(())
}
#[allow(non_snake_case)]
pub extern "C" fn Called_From_C() -> i32 {
let result = my_rust_function();
match result {
Ok(_) => 1,
Err(_) => -1,
}
}
Is there a more idiomatic way of converting my Result<(), Error> into the 1 / -1 return code?
I'd create an extension trait:
trait FfiError {
fn as_c_error(&self) -> i32;
}
impl<T, E> FfiError for Result<T, E> {
fn as_c_error(&self) -> i32 {
match self {
Ok(_) => 1,
Err(_) => -1,
}
}
}
Once it's brought into scope, you can call it like any other method:
pub extern "C" fn called_from_c() -> i32 {
my_rust_function().as_c_error()
}
See also:
Is there a way other than traits to add methods to a type I don't own?
You could use repr(transparent) to create a type where you could implement From and that still represent a i32, this allow to compile check that you transform your result correctly assuming you didn't have bug in your from() implementation so maybe add some unit tests.
type MyResult = Result<(), ()>;
fn my_rust_function() -> MyResult {
Ok(())
}
#[repr(transparent)]
pub struct CResult {
code: i32,
// code: libc::c_int, // if your C lib expect a `c_int` and not a `i32`
}
impl From<MyResult> for CResult {
fn from(result: MyResult) -> Self {
let code = match result {
Ok(_) => 1,
Err(_) => -1,
};
Self { code }
}
}
#[allow(non_snake_case)]
pub extern "C" fn Called_From_C() -> CResult {
let result = my_rust_function();
result.into()
}
You could also use enum with repr(i32):
#[repr(i32)]
pub enum CResult {
NoError = 1,
Error = -1,
}
impl From<MyResult> for CResult {
fn from(result: MyResult) -> Self {
match result {
Ok(_) => CResult::NoError,
Err(_) => CResult::Error,
}
}
}
In nightly, you could also implement Try:
#![feature(try_trait)]
use std::ops::Try;
type MyResult = Result<(), ()>;
fn my_rust_function() -> MyResult {
Ok(())
}
#[repr(i32)]
pub enum CResult {
NoError = 1,
Error = -1,
}
impl From<MyResult> for CResult {
fn from(result: MyResult) -> Self {
match result {
Ok(_) => CResult::NoError,
Err(_) => CResult::Error,
}
}
}
impl From<CResult> for MyResult {
fn from(cresult: CResult) -> Self {
match cresult {
CResult::NoError => Ok(()),
CResult::Error => Err(()),
}
}
}
impl Try for CResult {
type Ok = ();
type Error = ();
fn into_result(self) -> MyResult {
self.into()
}
fn from_ok(_: <Self as Try>::Ok) -> Self {
Self::NoError
}
fn from_error(_: <Self as Try>::Error) -> Self {
Self::Error
}
}
#[allow(non_snake_case)]
pub extern "C" fn Called_From_C() -> CResult {
let _ = my_rust_function()?;
CResult::NoError
}
Note: Be careful with the enumeration one, make sure your implementation is compatible. #[repr(libc::c_int)] is what we really want but I don't know any way to express this in Rust. So maybe a structure with repr(transparent) is more safe if the lib expect a c_int.

Move non-copyable struct across iterations

I am trying to access a variable inside a for loop. I can't implement Copy on the struct because it contains a String. How would I use the variable across iterations?
I get error E0382 when compiling. When I looked at the Rust documentation for the error, they mentioned using reference counting to solve the problem. Is this the only solution in my case?
#[derive(Clone)]
struct InputParser {
args: Vec<String>,
current: String,
consumed_quote: bool,
}
impl InputParser {
pub fn parse(input: String) -> Vec<String> {
let parser = InputParser {
args: Vec::new(),
current: String::new(),
consumed_quote: false,
};
for c in input.chars() {
match c {
'"' => parser.consume_quote(),
' ' => parser.consume_space(),
_ => parser.consume_char(c),
}
}
parser.end();
return parser.args;
}
pub fn consume_space(mut self) {
if !self.consumed_quote {
self.push_current();
}
}
pub fn consume_quote(mut self) {
self.consumed_quote = self.consumed_quote;
if self.consumed_quote {
self.push_current();
}
}
pub fn consume_char(mut self, c: char) {
self.current.push(c);
}
pub fn end(mut self) {
self.push_current();
}
pub fn push_current(mut self) {
if self.current.len() > 0 {
self.args.push(self.current);
self.current = String::new();
}
}
}
I want to access parser across iterations of the for loop.
[How do I] move [a] non-copyable struct across iterations
You don't, at least not trivially. Once you've moved the struct to a function, it's gone. The only way to get it back is for the function to give it back to you.
Instead, you most likely want to modify an existing struct inside the loop. You need to use a mutable reference for this:
use std::mem;
#[derive(Clone)]
struct InputParser {
args: Vec<String>,
current: String,
consumed_quote: bool,
}
impl InputParser {
fn consume_space(&mut self) {
if !self.consumed_quote {
self.push_current();
}
}
fn consume_quote(&mut self) {
self.consumed_quote = self.consumed_quote;
if self.consumed_quote {
self.push_current();
}
}
fn consume_char(&mut self, c: char) {
self.current.push(c);
}
fn end(&mut self) {
self.push_current();
}
fn push_current(&mut self) {
if self.current.len() > 0 {
let arg = mem::replace(&mut self.current, String::new());
self.args.push(arg);
}
}
}
fn parse(input: String) -> Vec<String> {
let mut parser = InputParser {
args: Vec::new(),
current: String::new(),
consumed_quote: false,
};
for c in input.chars() {
match c {
'"' => parser.consume_quote(),
' ' => parser.consume_space(),
_ => parser.consume_char(c),
}
}
parser.end();
parser.args
}
fn main() {}
Note that the previous way of taking the current argument would result in error[E0507]: cannot move out of borrowed content, so I switched to mem::replace. This prevents self.current from ever becoming an undefined value (which it was previously).
If you really want to pass everything by value, you need to return by value as well.
#[derive(Clone)]
struct InputParser {
args: Vec<String>,
current: String,
consumed_quote: bool,
}
impl InputParser {
fn consume_space(mut self) -> Self {
if !self.consumed_quote {
return self.push_current();
}
self
}
fn consume_quote(mut self) -> Self {
self.consumed_quote = self.consumed_quote;
if self.consumed_quote {
return self.push_current();
}
self
}
fn consume_char(mut self, c: char) -> Self {
self.current.push(c);
self
}
fn end(mut self) -> Self {
self.push_current()
}
fn push_current(mut self) -> Self {
if self.current.len() > 0 {
self.args.push(self.current);
self.current = String::new();
}
self
}
}
fn parse(input: String) -> Vec<String> {
let mut parser = InputParser {
args: Vec::new(),
current: String::new(),
consumed_quote: false,
};
for c in input.chars() {
parser = match c {
'"' => parser.consume_quote(),
' ' => parser.consume_space(),
_ => parser.consume_char(c),
}
}
parser = parser.end();
parser.args
}
fn main() {}
I believe this makes the API objectively worse in this case. However, you will see this style somewhat frequently with a builder. In that case, the methods tend to be chained together, so you never see a reassignment to the variable.

How to avoid redundant code when implementing Index and IndexMut in Rust

At the moment, implementing the std::ops::IndexMut trait on a type in Rust requires that I also implement the std::ops::Index trait as well. The bodies of these implementations end up being virtually identical. For example:
use std::ops::{Index, IndexMut};
enum IndexType {
A,
B,
}
struct Indexable {
a: u8,
b: u8,
}
impl Index<IndexType> for Indexable {
type Output = u8;
fn index<'a>(&'a self, idx: IndexType) -> &'a u8 {
match idx {
IndexType::A => &self.a,
IndexType::B => &self.b,
}
}
}
impl IndexMut<IndexType> for Indexable {
fn index_mut<'a>(&'a mut self, idx: IndexType) -> &'a mut u8 {
match idx {
IndexType::A => &mut self.a,
IndexType::B => &mut self.b,
}
}
}
fn main() {}
This works, and obviously for trivial types this isn't a serious problem, but for more complex types with more interesting indexing this quickly becomes laborious and error-prone. I'm scratching my head trying to find a way to unify this code, but nothing is jumping out at me, and yet I feel there has to/should be a way to do this without essentially having to copy and paste. Any suggestions? What am I missing?
Unfortunately, this cuts across a few things Rust really isn't good at right now. The cleanest solution I could come up with was this:
macro_rules! as_expr {
($e:expr) => { $e };
}
macro_rules! borrow_imm { ($e:expr) => { &$e } }
macro_rules! borrow_mut { ($e:expr) => { &mut $e } }
macro_rules! impl_index {
(
<$idx_ty:ty> for $ty:ty,
($idx:ident) -> $out_ty:ty,
$($body:tt)*
) => {
impl ::std::ops::Index<$idx_ty> for $ty {
type Output = $out_ty;
fn index(&self, $idx: $idx_ty) -> &$out_ty {
macro_rules! index_expr { $($body)* }
index_expr!(self, borrow_imm)
}
}
impl ::std::ops::IndexMut<$idx_ty> for $ty {
fn index_mut(&mut self, $idx: $idx_ty) -> &mut $out_ty {
macro_rules! index_expr { $($body)* }
index_expr!(self, borrow_mut)
}
}
};
}
enum IndexType { A, B }
struct Indexable { a: u8, b: u8 }
impl_index! {
<IndexType> for Indexable,
(idx) -> u8,
($this:expr, $borrow:ident) => {
match idx {
IndexType::A => $borrow!($this.a),
IndexType::B => $borrow!($this.b),
}
}
}
fn main() {
let mut x = Indexable { a: 1, b: 2 };
x[IndexType::A] = 3;
println!("x {{ a: {}, b: {} }}", x[IndexType::A], x[IndexType::B]);
}
The short version is: we turn the body of index/index_mut into a macro so that we can substitute the name of a different macro that, given an expression, expands to either &expr or &mut expr. We also have to re-capture the self parameter (using a different name) because self is really weird in Rust, and I gave up trying to make it work nicely.

Resources