Best practice to call module's function based on argument [duplicate] - rust

This question already has answers here:
Running functions based on user input in Rust
(2 answers)
Closed 5 months ago.
What's the best way to call a module's function based on the input argument?
mod programs;
fn run(day: i32) {
match day {
1 => programs::day_01::main(),
2 => programs::day_02::main(),
_ => println!(""),
}
}
fn main() {
run(1);
}

A macro can be used to generate a match statement with all names and corresponding functions.
macro_rules! dispatch_to {
($val:expr => {$($func:ident),*}) => {
match $val {
$(
stringify!($func) => $func(),
)*
_ => {},
}
}
}
But you still need to provide the list of available functions in dispatch():
fn dispatch(s: &str) {
// this is how you can use the macro
dispatch_to!(s => {func_1, func_2});
}
fn main() {
let s: String = "func_2".into();
dispatch(&s);
}
fn func_1(){
println!("Function 1");
}
fn func_2() {
println!("Function 2");
}
Another way may be to locate the symbol in the binary using dlopen and dlsym.

Related

How to use compile time constants to generate a function name in Rust? [duplicate]

Given the macro matching example, this shows how macros can match an argument.
I've made very minor changes here to use numbers:
macro_rules! foo {
(0 => $e:expr) => (println!("mode X: {}", $e));
(1 => $e:expr) => (println!("mode Y: {}", $e));
}
fn main() {
foo!(1 => 3);
}
Works, printing: mode Y: 3
However I would like to use a constant as an argument, can this be made to work:
const CONST: usize = 1;
macro_rules! foo {
(0 => $e:expr) => (println!("mode X: {}", $e));
(1 => $e:expr) => (println!("mode Y: {}", $e));
}
fn main() {
foo!(CONST => 3);
}
Is this possible in Rust?
Note, using a regular match statement isn't usable for me, since in my code each branch resolves to different types, giving an error.
So I'm specifically interested to know if a constant can be passed to a macro.
No.
Macros operate on the Abstract Syntax Tree, so they reason at the syntactic level: they reason about tokens and their spelling.
For example:
fn main() {
let v = 3;
}
In this case, the AST will look something like:
fn main
\_ let-binding v
\_ literal 3
If you ask a macro whether v is 3, it will look at you funny, and wonder why you would try comparing a variable name and a literal.
I'm fairly sure the answer is "no"; at macro expansion time all you have are token trees - expansion happens before evaluation, or even type inference/checking.
const CONST: usize = 0;
macro_rules! foo {
($i:ident => $e:expr) => {
if $i == 0 {
println!("mode X: {}", $e);
} else if $i == 1 {
println!("mode Y: {}", $e);
}
};
}
fn main() {
foo!(CONST => 3);
}
If you want use identifier in macro it needs to be ident tag and you can use if, else if blocks instead of match.

Is there a macro I can use to expect a variant of an enum and extract its data?

Given an enum like
struct Earth { water: usize }
struct Mars { redness: usize }
enum World {
Mars(Mars),
Earth(Earth),
}
A common pattern I write is
fn something_expecting_mars(planet: World) {
let mars = match planet {
World::Mars(data) => data,
_ => panic!("Shouldn't be here now"),
}
}
Is there a macro I can use to expect a variant of an enum and subsequently extract its data?
// rewriting to this
let mars = expect_v!(planet, World::Mars);
The standard library provides a macro for testing a match, but not one for extracting a value. However, it's fairly easy to write one:
macro_rules! expect_v {
($e:expr, $p:path) => {
match $e {
$p(value) => value,
_ => panic!("expected {}", stringify!($p)),
}
};
}
Playground
As suggested in answers to the related question brought up in the comments, you might want to decouple value extraction from the panic. In that case, return an Option instead and let the callers panic if they wish by calling unwrap():
macro_rules! extract {
($e:expr, $p:path) => {
match $e {
$p(value) => Some(value),
_ => None,
}
};
}
// ...
fn something_expecting_mars(planet: World) {
let mars = extract!(planet, World::Mars).unwrap();
}
Anything wrong with just using if let instead of match?
mars = if let World::Mars(data) = planet { data } else { panic!("Woot woot")}

Why can Rust not use const as literals? [duplicate]

Given the macro matching example, this shows how macros can match an argument.
I've made very minor changes here to use numbers:
macro_rules! foo {
(0 => $e:expr) => (println!("mode X: {}", $e));
(1 => $e:expr) => (println!("mode Y: {}", $e));
}
fn main() {
foo!(1 => 3);
}
Works, printing: mode Y: 3
However I would like to use a constant as an argument, can this be made to work:
const CONST: usize = 1;
macro_rules! foo {
(0 => $e:expr) => (println!("mode X: {}", $e));
(1 => $e:expr) => (println!("mode Y: {}", $e));
}
fn main() {
foo!(CONST => 3);
}
Is this possible in Rust?
Note, using a regular match statement isn't usable for me, since in my code each branch resolves to different types, giving an error.
So I'm specifically interested to know if a constant can be passed to a macro.
No.
Macros operate on the Abstract Syntax Tree, so they reason at the syntactic level: they reason about tokens and their spelling.
For example:
fn main() {
let v = 3;
}
In this case, the AST will look something like:
fn main
\_ let-binding v
\_ literal 3
If you ask a macro whether v is 3, it will look at you funny, and wonder why you would try comparing a variable name and a literal.
I'm fairly sure the answer is "no"; at macro expansion time all you have are token trees - expansion happens before evaluation, or even type inference/checking.
const CONST: usize = 0;
macro_rules! foo {
($i:ident => $e:expr) => {
if $i == 0 {
println!("mode X: {}", $e);
} else if $i == 1 {
println!("mode Y: {}", $e);
}
};
}
fn main() {
foo!(CONST => 3);
}
If you want use identifier in macro it needs to be ident tag and you can use if, else if blocks instead of match.

Is there an elegant way to rewrite getting or creating an Option using a `match` statement? [duplicate]

This question already has an answer here:
How to get an Option's value or set it if it's empty?
(1 answer)
Closed 2 years ago.
I boiled my code down to these lines:
#[derive(Debug)]
struct Config {
values: Vec<String>,
compiled: Option<Vec<String>>,
}
impl Config {
fn comp_values(&mut self) -> &Vec<String> {
if self.compiled.is_none() {
self.compiled = Some(self.values.clone());
}
self.compiled.as_ref().unwrap()
}
fn comp_values_match(&mut self) -> &Vec<String> {
match self.compiled {
Some(_) => self.compiled.as_ref().unwrap(),
None => {
self.compiled = Some(self.values.clone());
self.compiled.as_ref().unwrap()
}
}
}
}
fn main() {
let mut c = Config {
values: vec![String::from("a"), String::from("b")],
compiled: None,
};
println!("config before: {:?}", c);
println!("compiled: {:?}", c.comp_values());
println!("config after: {:?}", c);
}
What I would like to have is something like:
match self.compiled {
Some(v) => v,
None => {
// assemble v and assign it to self.compiled
v
},
}
just like in the closure chapter in the book. Is it possible in the book it only worked, because u32 implements the Copy trait? If I change the match line to match self.compiled.as_ref() the Some(v) => v, works. In the None arm I then can't assign like self.compiled = ... since I have have the variable 'open' (or referenced with self.compiled.as_ref()) in that block.
Are my assumptions correct? Is the comp_values() method the most elegant solution in that case?
Not a match statement, but I think you are looking for get_or_insert_with.

Using `pop3::POP3Stream::connect` to connect to runtime given `host`? [duplicate]

This question already has answers here:
How to convert a String into a &'static str
(4 answers)
Closed 7 years ago.
I'm trying to read input from the user, and then use it as the URL for the POP3 library. When converting the String that I get to a string slice, it doesn't live long enough to be used. This is strange to me for two reasons:
Because everything that uses the POP3 object is inside the same block, so the lifetime of the str slice should be that of the entire block, which would cover everything
I've tried almost every different code configuration I could think of, and to no avail, I get the same error every time.
extern crate pop3;
extern crate smtp;
extern crate openssl;
extern crate libc;
use openssl::ssl::{SslContext, SslMethod};
use pop3::POP3Stream;
use pop3::POP3Result::{POP3Stat, POP3List, POP3Message};
mod readline;
use readline::*;
fn main() {
let place = match readline("URL: ") { // Problem line
Some(input) => { // Problem line
let place: &'static str = &input[..]; // Problem line
let mut email_socket = match POP3Stream::connect(place, 995, Some(SslContext::new(SslMethod::Sslv23).unwrap())) { // Problem line
Ok(s) => s,
Err(e) => panic!("{}", e)
};
match readline("Username: ") {
Some(username) => {
match readline("Password: ") {
Some(password) => { email_socket.login(&*username, &*password); },
None => println!("Please enter a password.")
}
},
None => println!("Please enter a username.")
};
let stat = email_socket.stat();
match stat {
POP3Stat {num_email,
mailbox_size} => println!("num_email: {}, mailbox_size:{}", num_email, mailbox_size),
e => println!("There was an error signing into your server."),
}
let list_all = email_socket.list(None);
match list_all {
POP3List {emails_metadata} => {
for i in emails_metadata.iter() {
println!("message_id: {}, message_size: {}", i.message_id, i.message_size);
}
},
_ => println!("There was an error listing your messages."),
}
let message_25 = email_socket.retr(25);
match message_25 {
POP3Message{raw} => {
for i in raw.iter() {
println!("{}", i);
}
},
_ => println!("There was an error getting your 25th message."),
}
email_socket.quit();
},
None => { println!("Please enter a URL for your server."); }
};
}
The Problem
Your problem boils down to the use of static since the keyword basically says "keep this object around forever". This means that the lifetime of place, without a doubt, will live long after input — forever vs the scope of the block.
fn get() -> Option<String> {
Some("hello world".to_owned())
}
fn main() {
let data = match get() {
Some(input) => { let place : &'static str = &input[..]; },
None => { }
};
}
In the above we try to make place a static reference to a str, other other words; a reference that exists for the entire duration of our program. input on the other hand will definitely not exist for this amount of time, and therefor we get an error diagnostic.
<anon>:7:54: 7:59 error: `input` does not live long enough
<anon>:7 Some(input) => { let place : &'static str = &input[..]; },
The Solution
Remove the use of static, effectively saying that the lifetime of place is that of the block (which is a subset of the lifetime associated with input).
fn get() -> Option<String> {
Some("hello world".to_owned())
}
fn main() {
let data = match get() {
Some(input) => { let place : &str = &input[..]; },
None => { }
};
}
Further Digging
As it turns out, POP3Stream::connect accepts a &'static str as its first argument; this is really bad design since it will only accept string-literals.
impl Pop3Stream {
pub fn connect(host: &'static str, ...) -> Result<POP3Stream> {
...
}
}
https://github.com/mattnenterprise/rust-pop3/blob/master/src/pop3.rs
You can, however, hack your way around the issue by intentionally leaking the resource—effectively making it live "forever". Please note the usage of unsafe, and keep in mind that this is—by language design—considered to be just that.
fn get () -> Option<String> {
Some("hello world".to_owned ())
}
fn connect (host : &'static str) {
/* ... */
}
fn main() {
let data = match get() {
Some(input) => {
let place : &'static str = unsafe {
use std::mem; let x = mem::transmute(&input as &str);
mem::forget (x); x
};
connect(place);
},
None => { }
};
}

Resources