Can I define a macro which will expand into a function call? - rust

I've (naively) tried this, but it doesn't print anything to the screen:
macro_rules! foo {
($suffix:tt, $arg:expr) => {
concat!("foo", $suffix, "(", $arg, ")");
};
}
fn foo_i32(x: i32) {
println!("i32 {}", x);
}
fn foo_bool(x: bool) {
println!("bool {}", x);
}
fn main() {
foo!("bool", true);
foo!("i32", 1);
}

Yes, and no.
First of, concat! generates a string, so your code is essentially the same as if you wrote:
fn main() {
"foobool(true)";
"fooi32(1)";
}
which is a no-op.
To generate Rust code, the macro does not need to involve strings at all:
macro_rules! foo {
($suffix:tt, $arg:expr) => {
$suffix($arg);
};
}
which you could call as foo!(foo_bool, true);.
If however you want to construct the name foo_bool from foo and bool, you need to use concat_idents, which is currently unstable and unlikely to get stable any time soon (because it causes some hygiene issues):
#![feature(concat_idents)]
macro_rules! foo {
($suffix:tt, $arg:expr) => {
concat_idents!(foo_, $suffix)($arg);
};
}
fn foo_i32(x: i32) {
println!("i32 {}", x);
}
fn foo_bool(x: bool) {
println!("bool {}", x);
}
fn main() {
foo!(bool, true);
foo!(i32, 1);
}

Related

How can I get all implementations of a struct and call it?

This is my dummy code that doesn't actually work, I would like to know how, or is there a way to achieve this functionality.
Currently if I want to do this, I need to define an enum and match, which is inefficient and difficult to maintain.
pub fn mainrun() {
let aimpl = MyStruct {
Name: "bar".to_string(),
};
// dummy method collector
let some_dummy = impl_method_collector(&aimpl);
for k in some_dummy {
k();
}
/*expected to get
bar say one
bar say two
bar say three
*/
}
struct MyStruct {
Name: String,
}
impl MyStruct {
fn one_fn(&self) {
println!("{:?} say one", self.Name)
}
fn two_fn(&self) {
println!("{:?} say two", self.Name)
}
fn three_fn(&self) {
println!("{:?} say three", self.Name)
}
}
Here is how I achieve the same in Go. I want to achieve something like this with Rust.
package main
import "reflect"
func main() {
println("start")
astr := &MyStruct{"bar"}
v := reflect.ValueOf(astr)
vNums := v.NumMethod()
for i := 0; i < vNums; i++ {
v.Method(i).Call([]reflect.Value{})
}
/*expected to get
start
bar say one
bar say three
bar say tow
*/
}
type MyStruct struct {
Name string
}
func (m *MyStruct) FnOne() {
println(m.Name, "say one")
}
func (m *MyStruct) FnTow() {
println(m.Name, "say tow")
}
func (m *MyStruct) FnThree() {
println(m.Name, "say three")
}
You can do something similar using a macro that defines all the "route" methods along with any "global" methods that want to use the list of "routes":
macro_rules! make_routes {
($name:ident $(fn $method:ident (&$self:ident) { $($code:tt)* })*) => {
impl $name {
$(fn $method (&$self) { $($code)* })*
// Here you define the method (or methods) that operate on the list
// of "routes".
fn call_all (&self) {
$(self.$method();)*
}
}
}
}
Then you call it like this:
struct MyStruct {
a: i32,
}
make_routes!{
MyStruct
fn route1 (&self) {
println!("Route 1");
}
fn route2 (&self) {
println!("Route 2 (a = {})", self.a);
}
}
Playground
Note that if the methods you want to call take extra parameters, then you will need to list the parameter names for each method (but you don't need to list the types since they must be the same anyway):
macro_rules! make_routes {
($name:ident $(fn $method:ident (&$self:ident, $param:pat) { $($code:tt)* })*) => {
impl $name {
$(fn $method (&$self, $param: i32) { $($code)* })*
fn call_all (&self, param: i32) {
$(self.$method (param);)*
}
}
}
}
make_routes!{
MyStruct
fn route1 (&self, param) {
println!("Route 1 (param = {})", param);
}
fn route2 (&self, param) {
println!("Route 2 (a = {}, param = {})", self.a, param);
}
}
Playground
For more details on macros, you can read The Little Book of Rust Macros.

Which signature is most effective when using multiple conditions or Results? How to bubble errors correctly?

Introduction
I'm learning rust and have been trying to find the right signature for using multiple Results in a single function and then returning either correct value, or exit the program with a message.
So far I have 2 different methods and I'm trying to combine them.
Context
This is what I'm trying to achieve:
fn blur(image: DynamicImage, amount: &str) -> DynamicImage {
let amount = parse_between_or_error_out("blur", amount, 0.0, 10.0);
image.brighten(amount)
}
This is what I have working now, but would like to refactor.
fn blur(image: DynamicImage, amount: &str) -> DynamicImage {
match parse::<f32>(amount) {
Ok(amount) => {
verify_that_value_is_between("blur", amount, 0.0, 10.0);
image.blur(amount)
}
_ => {
println!("Error");
process::exit(1)
}
}
}
Combining these methods
Now here's the two working methods that I'm trying to combine, to achieve this.
fn parse<T: FromStr>(value: &str) -> Result<T, <T as FromStr>::Err> {
value.parse::<T>()
}
fn verify_that_value_is_between<T: PartialOrd + std::fmt::Display>(
name: &str,
amount: T,
minimum: T,
maximum: T,
) {
if amount > maximum || amount < minimum {
println!(
"Error: Expected {} amount to be between {} and {}",
name, minimum, maximum
);
process::exit(1)
};
println!("- Using {} of {:.1}/{}", name, amount, maximum);
}
Here's what I tried
I have tried the following. I realise I'm likely doing a range of things wrong. This is because I'm still learning Rust, and I'd like any feedback that helps me learn how to improve.
fn parse_between_or_error_out<T: PartialOrd + FromStr + std::fmt::Display>(
name: &str,
amount: &str,
minimum: T,
maximum: T,
) -> Result<T, <T as FromStr>::Err> {
fn error_and_exit() {
println!(
"Error: Expected {} amount to be between {} and {}",
name, minimum, maximum
);
process::exit(1);
}
match amount.parse::<T>() {
Ok(amount) => {
if amount > maximum || amount < minimum {
error_and_exit();
};
println!("- Using {} of {:.1}/{}", name, amount, maximum);
amount
}
_ => {
error_and_exit();
}
}
}
Currently this looks quite messy, probably I'm using too many or the wrong types and the error needs to be in two places (hence the inlined function, which I know is not good practice).
Full reproducible example.
The question
How to best combine logic that is using a Result and another condition (or Result), exit with a message or give T as a result?
Comments on any of the mistakes are making are very welcome too.
You can use a crate such as anyhow to bubble your events up and handle them as needed.
Alternatively, you can write your own trait and implement it on Result.
trait PrintAndExit<T> {
fn or_print_and_exit(&self) -> T;
}
Then use it by calling the method on any type that implements it:
fn try_get_value() -> Result<bool, MyError> {
MyError { msg: "Something went wrong".to_string() }
}
let some_result: Result<bool, MyError> = try_get_value();
let value: bool = some_result.or_print_and_exit();
// Exits with message: "Error: Something went wrong"
Implementing this trait on Result could be done with:
struct MyError {
msg: String,
}
impl<T> PrintAndExit<T> for Result<T, MyError> {
fn or_print_and_exit(&self) -> T {
match self {
Ok(val) => val,
Err(e) => {
println!("Error: {}", e.msg);
std::process::exit(1);
},
}
}
}
Here are a few DRY tricks.
tl;dr:
Convert other Errors into your unified error type(s) with impl From<ExxError> for MyError;
In any function that may result in an Error, use ? as much as you can. Return Result<???, MyError> (*). ? will utilize the implicit conversion.
(*) Only if MyError is an appropriate type for the function. Always create or use the most appropriate error types. (Kinda obvious, but people often treat error types as a second-class code, pun intended)
Recommendations are in the comments.
use std::error::Error;
use std::str::FromStr;
// Debug and Display are required by "impl Error" below.
#[derive(Debug)]
enum ProcessingError {
NumberFormat{ message: String },
NumberRange{ message: String },
ProcessingError{ message: String },
}
// Display will be used when the error is printed.
// No need to litter the business logic with error
// formatting code.
impl Display for ProcessingError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
ProcessingError::NumberFormat { message } =>
write!(f, "Number format error: {}", message),
ProcessingError::NumberRange { message } =>
write!(f, "Number range error: {}", message),
ProcessingError::ProcessingError { message } =>
write!(f, "Image processing error: {}", message),
}
}
}
impl Error for ProcessingError {}
// FromStr::Err will be implicitly converted into ProcessingError,
// when ProcessingError is needed. I guess this is what
// anyhow::Error does under the hood.
// Implement From<X> for ProcessingError for every X error type
// that your functions like process_image() may encounter.
impl From<FromStr::Err> for ProcessingError {
fn from(e: FromStr::Err) -> ProcessingError {
ProcessingError::NumberFormat { message: format!("{}", e) }
}
}
pub fn try_parse<T: FromStr>(value: &str) -> Result<T, ProcessingError> {
// Note ?. It will implicitly return
// Err(ProcessingError created from FromStr::Err)
Ok (
value.parse::<T>()?
)
}
// Now, we can have each function only report/handle errors that
// are relevant to it. ? magically eliminates meaningless code like
// match x { ..., Err(e) => Err(e) }.
pub fn parse_between<T>(value: &str, min_amount: T, max_amount: T)
-> Result<T, ProcessingError>
where
T: FromStr + PartialOrd + std::fmt::Display,
{
let amount = try_parse::<T>(value)?;
if amount > max_amount || amount < min_amount {
Err(ProcessingError::NumberRange {
message: format!(
"Expected value to be between {} and {} but received {}",
min_amount,
max_amount,
amount)
})
} else {
Ok(amount)
}
}
main.rs
use image::{DynamicImage};
use std::fmt::{Debug, Formatter, Display};
fn blur(image: DynamicImage, value: &str)
-> Result<DynamicImage, ProcessingError>
{
let min_amount = 0.0;
let max_amount = 10.0;
// Again, note ? in the end.
let amount = parse_between(value, min_amount, max_amount)?;
image.blur(amount)
}
// All processing extracted into a function, whose Error
// then can be handled by main().
fn process_image(image: DynamicImage, value: &str)
-> Result<DynamicImage, ProcessingError>
{
println!("applying blur {:.1}/{:.1}...", amount, max_amount);
image = blur(image, value);
// save image ...
image
}
fn main() {
let mut image = DynamicImage::new(...);
image = match process_image(image, "1") {
Ok(image) => image,
// No need to reuse print-and-exit functionality. I doubt
// you want to reuse it a lot.
// If you do, and then change your mind, you will have to
// root it out of all corners of your code. Better return a
// Result and let the caller decide what to do with errors.
// Here's a single point to process errors and exit() or do
// something else.
Err(e) => {
println!("Error processing image: {:?}", e);
std::process::exit(1);
}
}
}
Sharing my results
I'll share my results/answer as well for other people who are new to Rust. This answer is based on that of #Acidic9's answer.
The types seem to be fine
anyhow looks to be the de facto standard in Rust.
I should have used a trait and implement that trait for the Error type.
I believe the below example is close to what it might look like in the wild.
// main.rs
use image::{DynamicImage};
use app::{parse_between, PrintAndExit};
fn main() {
// mut image = ...
image = blur(image, "1")
// save image
}
fn blur(image: DynamicImage, value: &str) -> DynamicImage {
let min_amount = 0.0;
let max_amount = 10.0;
match parse_between(value, min_amount, max_amount).context("Input error") {
Ok(amount) => {
println!("applying blur {:.1}/{:.1}...", amount, max_amount);
image.blur(amount)
}
Err(error) => error.print_and_exit(),
}
}
And the implementation inside the apps library, using anyhow.
// lib.rs
use anyhow::{anyhow, Error, Result};
use std::str::FromStr;
pub trait Exit {
fn print_and_exit(self) -> !;
}
impl Exit for Error {
fn print_and_exit(self) -> ! {
eprintln!("{:#}", self);
std::process::exit(1);
}
}
pub fn try_parse<T: FromStr>(value: &str) -> Result<T, Error> {
match value.parse::<T>() {
Ok(value) => Ok(value),
Err(_) => Err(anyhow!("\"{}\" is not a valid value.", value)),
}
}
pub fn parse_between<T>(value: &str, min_amount: T, max_amount: T) -> Result<T, Error>
where
T: FromStr + PartialOrd + std::fmt::Display,
{
match try_parse::<T>(value) {
Ok(amount) => {
if amount > max_amount || amount < min_amount {
return Err(anyhow!(
"Expected value to be between {} and {} but received {}",
min_amount,
max_amount,
amount
));
};
Ok(amount)
}
Err(error) => Err(error),
}
}
Hopefully seeing this full implementation will help someone out there.
Source code.

Is there a way to pass an extra value to the panic handler?

I'm writing a UEFI OS loader, and I use the system table provided by efi_main in the panic handler to print a string on the console. Currently, I'm using a global static variable and a helper function to access it like this:
static SYSTEM_TABLE_WRAPPER: Lazy<Spinlock<Option<SystemTable>>> =
Lazy::new(|| Spinlock::new(None));
#[panic_handler]
fn panic(i: &PanicInfo<'_>) -> ! {
// SAFETY: The existing lock is forgotten. There is no way to access the lock from the panic
// handler.
unsafe { unlock_system_table() }
error!("{}", i);
loop {
x86_64::instructions::hlt();
}
}
pub fn _print(args: fmt::Arguments<'_>) {
let mut st = crate::system_table();
let mut stdout = st.con_out();
let _ = stdout.write_fmt(args);
}
#[macro_export]
macro_rules! println {
() => {
$crate::print!("\n");
};
($($arg:tt)*)=>{
$crate::print!("{}\n",core::format_args!($($arg)*));
}
}
#[macro_export]
macro_rules! print {
($($arg:tt)*) => {
$crate::io::_print(core::format_args!($($arg)*));
};
}
pub(crate) fn system_table<'a>() -> MappedSpinlockGuard<'a, uefi_wrapper::SystemTable> {
let st = SYSTEM_TABLE_WRAPPER.try_lock();
let st = st.expect("Failed to lock the global System Table.");
SpinlockGuard::map(st, |st| {
let st = st.as_mut();
let st = st.expect("The global System Table is not initialized.");
&mut st.0
})
}
Although this works correctly, I'd like to avoid using any global variables if possible. Is there a way to do that?
I dont think so. If its possible, any parameter would be a global as well. Making it more complex.
Global variables are ok for this. Create your own global panic object and give it to a new panic handler from the real one.
Because there seems to be no way to do that, I changed the way.
Firstly, I defined the uefi_print and uefi_println macros.
#[macro_export]
macro_rules! uefi_print{
($st:expr,$($arg:tt)*)=>{
$crate::io::_print($st,format_args!($($arg)*));
}
}
#[macro_export]
macro_rules! uefi_println {
($st:expr) => {
$crate::uefi_print!($st,"\n");
};
($st:expr,$($arg:tt)*)=>{
$crate::uefi_print!($st,"{}\n",format_args!($($arg)*));
}
}
#[doc(hidden)]
pub fn _print(st: &mut crate::SystemTable, args: fmt::Arguments<'_>) {
let mut con_out = st.con_out();
let _ = con_out.write_fmt(args);
}
These macros are similar to print and println macros, but the first parameter is the mutable reference to SystemTable. The example macro invocation:
#[no_mangle]
pub extern "win64" fn efi_main(h: uefi_wrapper::Handle, mut st: bootx64::SystemTable) -> ! {
let resolution = gop::set_preferred_resolution(&mut st);
uefi_println!(&mut st, "GOP info: {:?}", resolution,);
// ...
}
Secondly, I defined the uefi_panic macro:
#[macro_export]
macro_rules! uefi_panic {
($st:expr) => {
$crate::uefi_panic!($st, "explicit panic");
};
($st:expr,$($t:tt)+)=>{
$crate::uefi_println!($st,"panicked at '{}', {}:{}:{}",core::format_args!($($t)+),file!(),line!(),column!());
panic!();
}
}
#[panic_handler]
fn panic(_: &PanicInfo<'_>) -> ! {
loop {
x86_64::instructions::hlt();
}
}
The first parameter of the macro is also the mutable reference to SystemTable.
Thirdly, I defined a wrapper type for SystemTable that defines the additional method expect_ok.
#[repr(transparent)]
#[derive(Debug)]
pub struct SystemTable(uefi_wrapper::SystemTable);
impl SystemTable {
// ...
/// # Panics
///
/// This method panics if `result` is [`Err`].
pub fn expect_ok<T, E: fmt::Debug>(&mut self, result: Result<T, E>, msg: &str) -> T {
match result {
Ok(val) => val,
Err(e) => {
uefi_panic!(self, "{}: {:?}", msg, e);
}
}
}
}
Now I prefer this way to the previous method. The exit boot services function can move the ownership of SystemTable and ImageHandle. This way prevents these types from being used after calling it. After all, I should not create a global static SystemTable since it won't be valid after exiting the boot services.

How to return when ref and ownership transfer both won't work

so, if I return this
self.string_ref.unwrap().as_ref()
compiler will say
error[E0515]: cannot return value referencing temporary value
returns a value referencing data owned by the current function
if I return this
*self.string_ref.unwrap().as_ref()
the compiler will say
error[E0507]: cannot move out of borrowed content
this is just drove me crazy
here is the code: (playground)
use std::ptr::NonNull;
struct A {
string_ref: Option<NonNull<String>>,
}
struct Number {
num: i32
}
impl A {
fn hello() {
}
fn give_me_string(&self) -> String {
unsafe {
*self.string_ref.unwrap().as_ref()
}
}
}
fn main() {
let a = A {
string_ref: NonNull::new(&mut String::from("hello world") as *mut String)
};
let t = a.give_me_string();
println!("{}", t)
}
Stripping your example to the bare minimum:
struct A {
string_ref: Option<NonNull<String>>,
}
impl A {
fn give_me_string(&self) -> String {
unsafe {
*self.string_ref.unwrap().as_ref()
}
}
}
There are a few errors here:
The most obvious one is that you're trying to take ownership of self.string_ref, even though you've only borrowed self.
To solve this you'll want to use a match statement, which allows you to destructure self.string_ref and not consume it:
fn give_me_string(&self) -> String {
unsafe {
match self.string_ref {
Some(x) => x.as_ref(),
None => panic!("Had no `string_ref`!")
}
}
}
as_ref returns &T, so you can't return an owned string, instead you need to either clone it and then return an owned string, or take reference to it:
//Option one: Clone contents
match self.string_ref {
Some(ref x) => x.as_ref().clone(),
_ => //...
}
//Option two: Return reference.
fn give_me_string(&self) -> &str {
unsafe {
match &self.string_ref {
Some(x) => x.as_ref() as _,
_ => //...
}
}
}
To address another problem mentioned in the comments, you have the following statement in your main function:
string_ref: NonNull::new(&mut String::from("hello world") as *mut String)
This will cause UB due to its nature. You are forming a String by using String::from, but are not storing its value anywhere and are instead immediately casting into a pointer. This will free the String at the end of the line, causing UB.
So I basically figured out what's going on, thanks to #Optimistic Peach
fn give_me_string(&self) -> &String {
unsafe {
match self.string_ref {
Some(x) => &*(x.as_ptr() as *const _), //without ref
Some(ref x) => x.as_ptr(), // with ref
None => panic!("hello?")
}
}
}

Macro that wraps an arbitrary number of types

Is it possible to write a macro that defines an enum which wraps an arbitrary number of (distinct) input types?
I'd like to do a kind of type-level match.
type_switch!(i32 => println!("integer"), f32 => println!("float"), Foo => println!("foo"))
This would expand to:
{
enum Wrapper {
Variant1(i32),
Variant2(f32),
Variant3(Foo),
}
// impl From<i32>, From<f32>, From<Foo> for Wrapper
|x: Wrapper| match x {
Wrapper::Variant1(x) => println!("integer"),
Wrapper::Variant2(x) => println!("float"),
Wrapper::Variant3(x) => println!("foo"),
}
}
so that I can write like
let switch = type_switch!(i32 => println!("integer"), f32 => println!("float"), Foo => println!("foo"));
switch(32.into()); // prints "integer"
switch(24.0.into()); // prints "float"
Define a trait within your macro and implement it for each type:
macro_rules! type_switch {
($($ty: ty => $expr: expr),+) => {{
trait TypeMatch {
fn type_match(self);
}
$(
impl TypeMatch for $ty {
fn type_match(self) {
$expr
}
}
)+
TypeMatch::type_match
}}
}
Notice that the first time you call the function the compiler will bind the type so that subsequent calls must be the same type:
struct Foo;
fn main() {
let s = type_switch! {
i32 => { println!("i32"); },
f32 => { println!("f32"); },
Foo => { println!("Foo"); }
};
s(0);
s(Foo); // Error!
}
If you need to be able to call it with different types, this can be fixed (at a small cost) by using a trait object for dynamic dispatch:
macro_rules! type_switch {
($($ty: ty => $expr: expr),+) => {{
trait TypeMatch {
fn type_match(&self);
}
$(
impl TypeMatch for $ty {
fn type_match(&self) {
$expr
}
}
)+
|value: &dyn TypeMatch| {
value.type_match()
}
}}
}
struct Foo;
fn main() {
let s = type_switch! {
i32 => { println!("i32"); },
f32 => { println!("f32"); },
Foo => { println!("Foo"); }
};
s(&0);
s(&Foo);
}
Also notice that you have to pass references instead of values.
It can make sense to write wrapper types as you have proposed, but only if the type is needed in larger parts of your code.
Your specific example would define a new enum every time you use the macro, move the value into the new enum and then immediately throw it away.
That's not a idiomatic approach and if that is indeed your imagined use I'd recommend looking for different options.
That said, I have used wrapper types on a number of occasions.
Something like this will work for declaring a wrapper:
macro_rules! declare_wrapper {
(
$enum_name:ident {
$( $variant_name:ident( $typ:ty : $description:expr ) ),*
}
)=> {
pub enum $enum_name {
$(
$variant_name($typ),
)*
}
$(
impl From<$typ> for $enum_name {
fn from(value: $typ) -> Self {
$enum_name::$variant_name(value)
}
}
)*
impl $enum_name {
fn describe(&self) -> &'static str {
match self {
$(
&$enum_name::$variant_name(_) => $description,
)*
}
}
}
};
}
declare_wrapper!( MyWrapper {
MyInt(i64 : "int"),
MyString(String : "string")
});
fn main() {
let value = MyWrapper::from(22);
println!("{}", value.describe());
}
You can also extend this to add additional methods or trait impls that you need.
I've done similar things quite often.

Resources