Как превратить значение ToString в & str во время компиляции? - PullRequest
0 голосов
/ 11 декабря 2018

TL; DR: Можно ли создать const … &str из const T, где T : ToString?


Я хотел бы указать значения по умолчанию при использованииclap.Однако для clap требуется default_value как &str, а не как примитивный тип:

pub fn default_value(self, val: &'a str) -> Self

Поэтому я не могу использовать ранее определенный const, если онне &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();
     …
}

Обходной путь должен использовать вместо него &str:

/// 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();
     …
}

Однако две константы могут легко выйти из синхронизации:

/// The application's default port.
pub const DEFAULT_LISTENER_PORT : u16 = 4579;

const DEFAULT_LISTENER_PORT_STR : &str = "12345"; // whoops

Поэтому я хотел бы сгенерировать последнее из первого с помощью некоторой магической функции или макроса:

/// The application's default port.
pub const DEFAULT_LISTENER_PORT : u16 = 4579;

const DEFAULT_LISTENER_PORT_STR : &str = magic!(DEFAULT_LISTENER_PORT);

Примечание: С std::string::ToString::to_string не const, он выходит за рамки, но может обеспечить обходной путь в основном, например

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

Но это не совсем эргономично.

Есть ли какой-нибудь стандартный макросили функция, которую я пропускаю, или еще нет определенного языка для обеспечения этой функциональности?

1 Ответ

0 голосов
/ 11 декабря 2018

Вы можете использовать макрос для одновременного определения целого числа и строки порта, используя 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);
}

Или, если вы хотите более общий:

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