Python to Rust Code converter

choose = int(input(quest + '[INPUT]' + mc + ' What do you want to do? >> ' + rs))
if choose == 1:
session = sessions.FuturesSession(max_workers=1000)
print('')
input_file = input(quest + '[INPUT]' + mc + ' What is name of your alt file >> ' + rs)
prox_input = input(quest + '[INPUT]' + mc + ' Do you want to use proxies? (y/n) >> ' + rs)
if prox_input == 'y':
proxy_file = input(quest + '[INPUT]' + mc + ' What is name of your proxy file >> ' + rs)
f = open(proxy_file, 'r')
proxies = f.read()
f.close()
I need the best code converted in Rust because I want to learn how to clean code and powerful. Thanks in advance!
Also "mc, rs" are colors, how to add them too?

You should follow the Forum Code Formatting and Syntax Highlighting guidelines that are pinned to the top of this forum (so that all new posters should see them). To post python code, on the first line, immediately after the three backticks, just add the language-name python.

choose = int(input(quest + '[INPUT]' + mc + ' What do you want to do? >> ' + rs))
if choose == 1:
    session = sessions.FuturesSession(max_workers=1000)
    print('')
    input_file = input(quest + '[INPUT]' + mc + ' What is name of your alt file >> ' + rs)
    prox_input = input(quest + '[INPUT]' + mc + ' Do you want to use proxies? (y/n) >> ' + rs)
    if prox_input == 'y':
        proxy_file = input(quest + '[INPUT]' + mc + ' What is name of your proxy file >> ' + rs)
        f = open(proxy_file, 'r')
        proxies = f.read()
        f.close()
2 Likes
  • (Thanks @TomP for formatting it)

I don't know of an automatic transpiler tool as of yet, but given that both have high-level language idioms, the "translation" isn't that hard. The intersting parts, however, would be to make it idiomatic.

So let's iterate a few changes form within Python itself, that will better match Rust patterns.

input() function

Basically it prompts the user with a message, and then reads a line of input:

use ::std::fmt::Display;

fn input (msg: impl Display) -> String
{
    use ::std::io::{BufRead, stdin, stdout, Write};

    print!("{}", msg);
    let _ = stdout().flush();
    let mut input = String::new();
    stdin().lock().read_line(&mut input)
        .expect("Got invalid UTF-8 input")
    ;
    // trim the trailing `\n`, or let ctrl+D exit the menu
    if input.pop().is_none() { panic!("Closed stdin"); }
    input
}

int() function

This is achieved through the .parse() method on a string, which is generic (to be fed using ::<Type> syntax, called turbofish, or thourhg type inference) on the type of thing we want to be parsing. For instance, an unsigned integer here:

// turbofish
let n = "...".parse::<u32>().expect("Failed to parse an integer");
// type inference
let n: u32 = "...".parse().expect("Failed to parse an integer");

// Alternative syntax to method syntax:
use ::core::str::FromStr;

let int = u32::from_str;
    /* or:
    let int = |s| u32::from_str(s); // */
    /* or:
    fn int (s: &str) -> Option<u32>
    {
        u32::from_str(s).ok()
    } // */
let n = int("...").expect("Failed to parse an integer");

Note that both .parse() and Type::from_str return a Result, to signify a recoverable error when the parsing fails (e.g., fed "a" and tried to parse it as a (decimal) integer). In these basic examples I have used expect("Error message") to transform that recoverable error into an "unrecoverable" one, which does automagically bubble up to the user (interrupting the flow of the program (but in a controlled manner)). That's a lazy thing to do, but convenient for basic examples or scripts (or unexpect / unreachable errors).

In this case, since you are interacting with what we will expect to be a human, typos are a reality, and you don't want to crash / interrupt the program whenever a typo is written. Instead, a smoother behaviour is to loop again with the question. So let's have a helper function for the int(input(...)) pattern:

#[macro_use] extern crate fstrings; // imitate Python 3 f-strings

use ::std::str::FromStr;

trait Input : FromStr
where
    Self::Err : Display, // a parsing error can be displayed as a meaningful error message
{
    /// Loop until obtaining a parseable result
    fn input (s: impl AsRef<str>) -> Self
    {
        let s: &str = s.as_ref();
        loop {
            let ref user_input = input(s)
            match Self::from_str(user_input) {
                | Ok(parsed_value) => {
                    return parsed_value;
                },
                | Err(err_msg) => {
                    let ty_name = ::core::any::type_name::<Self>();
                    eprintln_f!("\
                        Error, failed to parse {user_input:?} \
                        as a {ty_name}: {err_msg}.\
                    ");
                    // continue; /* loop again */
                },
            }
        }
    }
}
impl<T : FromStr> Input for T
where
    Self::Err : Display, // a parsing error can be displayed as a meaningful error message
{}
  • I have used an external dependency for some enhanced functionality.
    In this case, it's the fstrings crate, to get something very similar to Python
    fstrings, for a more Python-esque feeling.
    To add it, run cargo search fstrings and copy the fstrings = "..." line below the [dependencies] section in your Cargo.toml file.

    • Or, run cargo install cargo-edit to install the add subcommand for cargo,
      and then run:

      cargo add fstrings
      

That, coupled with type int = i64;, allows us to write:

match int::input("prompt here") {
    | 0 => { /* Handle 0 here */ },
    | 1 => { /* Handle 1 here */ },
    | anything_else => { /* Handle `anything_else ≠ 0, 1` here */ },
}

Demo

asciicast

sessions.FuturesSession

I won't handle that one in this post, since it would deserve its own lengthy post

open(...) ... .close(...)

This is actually an anti-pattern even in Python, since you don't have the close() call in a finally block, so if some exception is raised, your file will remain open.

The lengthy way to fix that in Python is, as mentioned, with a finally:

f = open(proxy_file, 'r'):
try:
    # do stuff with f
    proxies = f.read()
finally:
    f.close()

Now, that is quite a mouthful, especially when other exception handling is involved.
For this, Python offers something very close to C++ / Rust's destructor / finalizer runs (RAII): with blocks:

with open(proxy_file, 'r') as f:
    proxies = f.read()
# file is auto-closed by now

In Rust, the good news, is that by using destructors (called drop glue), you don't have to worry about writing antipatterns.

You can write Python's equivalent of the above as:

let mut proxies = String::new();
match open(proxy_file).expect("Failed to open the file") { ref mut f => {
    f   .read_to_string(&mut proxies)
        .unwrap_or_else(|err_msg| panic_f!(
            "Failed to read {proxy_file:?} contents: {err_msg}"
        ))
    ;
}}
// file is auto-closed by now

which is the most transparent way to write a scoped binding. But that match expr { var => { ... }} ins't that frequent in practice among Rust code, since there is a more "friendly-looking" syntax which almost the same semantics:

let mut proxies = String::new();
{
    let ref mut f = open(proxy_file).expect("Failed to open the file");
    f   .read_to_string(&mut proxies)
        .unwrap...
    ;
}
// file is auto-closed by now

Or even the "naïve" anti-pattern looking syntax does the right thing:

let mut proxies = String::new();
let mut f = open(proxy_file).expect...;
f.read_to_string...;
drop(f); // explicitely close the file. If the previous line failed (`panic!`), it would have auto-closed the file anyways.

Note that for this very pattern Rust does offer a one-line: ::std::fs::read_to_string(...)


Rewrite it in Rust: Python

Original Python code
choose = int(input(quest + '[INPUT]' + mc + ' What do you want to do? >> ' + rs))
if choose == 1:
    session = sessions.FuturesSession(max_workers=1000)
    print('')
    input_file = input(quest + '[INPUT]' + mc + ' What is name of your alt file >> ' + rs)
    prox_input = input(quest + '[INPUT]' + mc + ' Do you want to use proxies? (y/n) >> ' + rs)
    if prox_input == 'y':
        proxy_file = input(quest + '[INPUT]' + mc + ' What is name of your proxy file >> ' + rs)
        f = open(proxy_file, 'r')
        proxies = f.read()
        f.close()
/* input() stuff above */

#[allow(nonstandard_style)]
type int = i64;

enum YesOrNo {
    Yes,
    No,
}

#[derive(Debug, ::thiserror::Error)] // `cargo add thiserror`
#[error("Expected `y`, `Y`, `n`, or `N`")]
struct ParseYesOrNoError;

impl FromStr for YesOrNo {
    type Err = ParseYesOrNoError;

    fn from_str (input: &str) -> Result<YesOrNo, Self::Err>
    {
        match input {
            | "y" | "Y" => Ok(YesOrNo::Yes),
            | "n" | "N" => Ok(YesOrNo::No),
            | _ => Err(ParseYesOrNoError),
        }
    }
}

// ...

// main:
let quest = ...;
let mc = ...;
let rs = ...;
let wrap = |question| f!("{quest}[INPUT]{mc} {question} >> {rs}");
match int::input(wrap("What do you want to do?")) {
    | 1 => {
        let ask = |question: &str| input(wrap(question));
        let session = ...;
        println!();
        let input_file = ask("What is name of your alt file");
        let mut proxies = None;
        match YesNo::input(wrap("Do you want to use proxies? (y/n)")) {
            | YesOrNo::Yes => {
                let ref proxy_file = ask("What is name of your proxy file");
                proxies = Some(
                    fs::read_to_string(proxy_file)
                        .unwrap_or_else(|err_msg| panic_f!(
                            "Failed to read the contents of {proxy_file:?}: {err_msg}."
                        ))
                    )
                );
            },
            | YesOrNo::No => {
                // ...
            },
        }
    },

    | any_other_int => {
        // ...
    },
}

asciicast

10 Likes

Thank you, I really learned something. Also, at mc, rs what I add in "" to make them colors? I mean sentences to be colored. Like: "What do you want to do?" Also how I add colors to sentences?

To colour text in the terminal it is advisable to use an external crate, since that will lead to the most portable solution. Here are the some popular crates for that purpose:

1 Like