Interpolate string in const function - rust

Im trying to make a function similar to this
pub const fn insert(num1:i32, num2:i32) -> &'static str {
formatcp!("n1:{}, n2:{}" , num1, num2)
}
But num1/num2 are not const.
I think this would be possible as a macro but im not experienced in macros.

Does it help?
macro_rules! insert {
($n1:expr, $n2:expr) => {
concat!("n1: ", $n1, " , n2: ", $n2)
};
}
const TEST_1: &str = insert!(1, 2);
const TEST_2: &str = insert!(2, 3);
fn main() {
println!("{}", TEST_1);
println!("{}", TEST_2);
}

Related

How do I use an impl function in the main function in rust?

I want to use a function in the main function in a rust program that I am building to help me learn rust and come up with an error: self value is a keyword only available in methods with a self parameterrustc(E0424). What can I fix in my code so that this error does not happen?
pub use crate::user_account::user_account;
use rand::Rng;
#[allow(dead_code)]
pub trait UserInfo {
fn user_info(&mut self);
fn acc_no(&mut self);
fn yes(self);
fn bank_new_user(self);
}
pub struct NewUser {
age: String,
new_user: String,
account: String,
account_number: i32,
routing_number: i32,
select: String,
}
impl UserInfo for NewUser {
fn user_info(&mut self) {
self.age = String::new();
self.new_user = String::new();
println!("What is your name?");
print!("Name: ");
std::io::stdin().read_line(&mut self.new_user);
println!(" ");
println!("Hello {}, What is your age? ", self.new_user);
std::io::stdin().read_line(&mut self.age);
let age2: String = self.age.trim().into();
}
fn acc_no(&mut self) {
println!(
"We will generate a new account number \
and routing number for you."
);
self.account_number = rand::thread_rng().gen_range(10000000..99999999);
println!("Your account number is {}", self.account_number);
self.routing_number = rand::thread_rng().gen_range(10000000..99999999);
println!("Your account routing number is {}", self.routing_number);
}
fn yes(self) {
NewUser::user_info(&mut self);
NewUser::acc_no(&mut self);
}
//function I want to use in main.
fn bank_new_user(self) {
self.account = String::new();
println!("Would you like to make a new account with us today?");
loop {
println!(
" yes: continue to application, no: continue browsing , \
or exit: to exit"
);
self.account.clear();
std::io::stdin()
.read_line(&mut self.account)
.expect("please type yes, no or exit.");
let account = self.account.trim();
match account {
"yes" => {
self.yes();
break;
}
"no" => {
println!("You do not need an account to continue browsing.");
println!("Have a wonderful day and thank you for considering Mars Banking!");
break;
}
"exit" => {
println!(
"Thank you for choosing Mars Banking for your banking needs!\
Have a wonderful day!"
);
break;
}
_ => {
println!("Error! Enter yes, no, or exit.")
}
}
}
}
}
pub mod new_user;
mod settings;
mod user_account;
pub use crate::settings::settings;
pub use crate::user_account::user_account;
use new_user::NewUser;
use new_user::UserInfo;
fn main() {
loop{
let mut select = String::new();
println!("Welcome to Mars Banking!");
println!("What would you like to do today?");
println!("Create a new account: 1\nLogin: 2\nSettings: 3\nExit: 4");
select.clear();
std::io::stdin().read_line(&mut select);
let select = select.trim();
match select {
//Here is where the error happens.
"1" => NewUser::bank_new_user(self),
"2" => user_account(),
"3" => settings(),
"4" => break,
_ => {}
}
}
}
The conventional pattern for this sort of constructor is a static method that doesn't take a self argument, like this:
impl NewUser {
fn bank_new_user() {
let mut new_user = NewUser { /* initialize the fields */ };
// Edit or use new_user as necessary
}
}
you can see an example of this here, in the methods defined for Point:
struct Point {
x: f64,
y: f64,
}
// Implementation block, all `Point` associated functions & methods go in here
impl Point {
// This is an "associated function" because this function is associated with
// a particular type, that is, Point.
//
// Associated functions don't need to be called with an instance.
// These functions are generally used like constructors.
fn origin() -> Point {
Point { x: 0.0, y: 0.0 }
}
// Another associated function, taking two arguments:
fn new(x: f64, y: f64) -> Point {
Point { x: x, y: y }
}
}
notice how niether origin nor new take self as an argument.

Get a raw vec with field names of any struct with a custom derive macro in Rust

I am trying to write some code that could be able to write a method that returns me a Vec with the names of the fields of a struct.
Code snippet below:
# Don't forget about dependencies if you try to reproduce this on local
use proc_macro2::{Span, Ident};
use quote::quote;
use syn::{
punctuated::Punctuated, token::Comma, Attribute, DeriveInput, Fields, Meta, NestedMeta,
Variant, Visibility,
};
#[proc_macro_derive(StructFieldNames, attributes(struct_field_names))]
pub fn derive_field_names(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let ast: DeriveInput = syn::parse(input).unwrap();
let (vis, ty, generics) = (&ast.vis, &ast.ident, &ast.generics);
let names_struct_ident = Ident::new(&(ty.to_string() + "FieldStaticStr"), Span::call_site());
let fields = filter_fields(match ast.data {
syn::Data::Struct(ref s) => &s.fields,
_ => panic!("FieldNames can only be derived for structs"),
});
let names_struct_fields = fields.iter().map(|(vis, ident)| {
quote! {
#vis #ident: &'static str
}
});
let mut vec_fields: Vec<String> = Vec::new();
let names_const_fields = fields.iter().map(|(_vis, ident)| {
let ident_name = ident.to_string();
vec_fields.push(ident_name);
quote! {
#vis #ident: -
}
});
let names_const_fields_as_vec = fields.iter().map(|(_vis, ident)| {
let ident_name = ident.to_string();
// vec_fields.push(ident_name)
});
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let tokens = quote! {
#[derive(Debug)]
#vis struct #names_struct_ident {
#(#names_struct_fields),*
}
impl #impl_generics #ty #ty_generics
#where_clause
{
#vis fn get_field_names() -> &'static str {
// stringify!(
[ #(#vec_fields),* ]
.map( |s| s.to_string())
.collect()
// )
}
}
};
tokens.into()
}
fn filter_fields(fields: &Fields) -> Vec<(Visibility, Ident)> {
fields
.iter()
.filter_map(|field| {
if field
.attrs
.iter()
.find(|attr| has_skip_attr(attr, "struct_field_names"))
.is_none()
&& field.ident.is_some()
{
let field_vis = field.vis.clone();
let field_ident = field.ident.as_ref().unwrap().clone();
Some((field_vis, field_ident))
} else {
None
}
})
.collect::<Vec<_>>()
}
const ATTR_META_SKIP: &'static str = "skip";
fn has_skip_attr(attr: &Attribute, path: &'static str) -> bool {
if let Ok(Meta::List(meta_list)) = attr.parse_meta() {
if meta_list.path.is_ident(path) {
for nested_item in meta_list.nested.iter() {
if let NestedMeta::Meta(Meta::Path(path)) = nested_item {
if path.is_ident(ATTR_META_SKIP) {
return true;
}
}
}
}
}
false
}
The code it's taken from here. Basically I just want to get those values as a String, and not to access them via Foo::FIELD_NAMES.some_random_field, because I need them for another process.
How can I achieve that?
Thanks

How can I concatenate a string to an ident in a macro derive?

I need to create a macro derive where the name is part of the function name. (This code does not work, it is only to show the problem)
fn impl_logic(ast: &syn::DeriveInput) -> TokenStream {
let name:&syn::Ident = &ast.ident;
let gen = quote! {
pub fn #name_logic() -> Arc<Mutex<UiAplicacion>> {
...
}
};
gen.into()
}
How can I do this?
Based on quote's docs, you can construct a new identifier with syn::Ident:
let fname = format!("{}_logic", name);
let varname = syn::Ident::new(&fname, ident.span());
and then interpolate it:
let gen = quote! {
pub fn #varname() -> Arc<Mutex<UiAplicacion>> {
...
}

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

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);
}

How do I turn a ToString value into a &str at compile time?

TL;DR: Is it possible to create a const … &str from a const T where T : ToString?
I like to provide default values when I use clap. However, clap needs the default_value as &str, not as a primitive type:
pub fn default_value(self, val: &'a str) -> Self
Therefore, I cannot use a previously defined const if it is not a &str:
use clap::{App, Arg};
/// The application's default port.
pub const DEFAULT_LISTENER_PORT : u16 = 12345;
fn main () {
let matches = App::new(env!("CARGO_PKG_NAME"))
.version(env!("CARGO_PKG_VERSION"))
.arg(Arg::with_name("port")
.short("p")
.value_name("PORT")
.default_value(DEFAULT_LISTENER_PORT) // < error here
).get_matches();
…
}
The workaround is to use a &str instead:
/// The application's default port.
pub const DEFAULT_LISTENER_PORT : u16 = 12345;
// Not public, since implementation detail.
const DEFAULT_LISTENER_PORT_STR : &str = "12345";
fn main () {
let matches = App::new(env!("CARGO_PKG_NAME"))
.version(env!("CARGO_PKG_VERSION"))
.arg(Arg::with_name("port")
.short("p")
.value_name("PORT")
.default_value(DEFAULT_LISTENER_PORT_STR)
).get_matches();
…
}
However, the two constants can get easily out of sync:
/// The application's default port.
pub const DEFAULT_LISTENER_PORT : u16 = 4579;
const DEFAULT_LISTENER_PORT_STR : &str = "12345"; // whoops
Therefore, I'd like to generate the latter from the former by some magic function or macro:
/// The application's default port.
pub const DEFAULT_LISTENER_PORT : u16 = 4579;
const DEFAULT_LISTENER_PORT_STR : &str = magic!(DEFAULT_LISTENER_PORT);
Note: Since std::string::ToString::to_string isn't const, it's out of scope but would provide a workaround in main, e.g.
let port_string = DEFAULT_LISTENER_PORT.to_string();
let matches = App::new(env!("CARGO_PKG_NAME"))
.version(env!("CARGO_PKG_VERSION"))
.arg(Arg::with_name("port")
.short("p")
.value_name("PORT")
.default_value(&port_string)
).get_matches();
But that's not really ergonomic either.
Is there any standard macro or function that I'm missing, or is there no language-defined way yet to provide that functionality?
You can use a macro to define the port integer and string at the same time using stringify!:
macro_rules! define_port {
($port:expr) => {
pub const DEFAULT_LISTENER_PORT : u16 = $port;
const DEFAULT_LISTENER_PORT_STR : &str = stringify!($port);
}
}
define_port!(4579);
fn main() {
println!("{}:{}", DEFAULT_LISTENER_PORT, DEFAULT_LISTENER_PORT_STR);
}
Or, if you want a more generic one:
pub struct DefaultParam<T> {
value: T,
name: &'static str,
}
macro_rules! define {
( $name:ident : $t:ty = $val:expr ) => {
pub const $name: DefaultParam<$t> = DefaultParam {
value: $val,
name: stringify!($val),
};
}
}
define!(PORT: u32 = 1234);
fn main() {
println!("{} has the value: {}", PORT.name, PORT.value);
}

Resources