Создать тип, который в основном является String, но несовместим с String? - PullRequest
4 голосов
/ 09 октября 2019

Я заметил, что в моих проектах я использую много типов String, где я на самом деле не имею в виду String s, а конкретные типы значений, содержащихся в String, которые нельзя отличить друг от друганапример, с помощью регулярных выражений, но их следует отличать от перспективы кода. Есть ли способ заставить компилятор мне помочь? Например, рассмотрим эту функцию:

fn do_something(val1: String, val2: String) {}

fn main() {
    let val1: String = "hello";
    let val2: String = "hello2";
    do_something(val1, val2);
}

Есть ли способ создать SpecificStringType1 и SpecificStringType2, чтобы первый вызов do_something компилировался, а второй - нет?

fn do_something(val1: SpecificStringType1, val2: SpecificStringType2) {}

fn main() {
    let val1: SpecificStringType1 = "hello";
    let val2: SpecificStringType2 = "hello2";
    let val3: SpecificStringType2 = "hello2";
    let val4: SpecificStringType2 = "hello2";
    do_something(val1, val2);
    do_something(val3, val4);
}

Ответы [ 2 ]

1 голос
/ 17 октября 2019

Вы можете просто обернуть String структурой кортежей, создав новый отдельный тип:

// This is a custom type which just wraps String,
// however you can choose what it derives again.
#[derive(Clone, Debug)]
struct MyString(String);

// Since they are distinct types, type check will prevent using
// String as MyString and vice versa.
fn f1(s: &String, t: &String) -> bool {
    return true;    
}

fn f2(s: &String, t: &MyString) -> bool {
    return false;
}

fn main() {
    let s: String = "hello".to_string();
    let ms = MyString("goodbye".to_string());
    let MyString(ms_inner) = ms.clone();

    println!("{:?}", &s);
    println!("{:?}", &ms);

    // Here is proof that it works.
    println!("{}", f1(&s, &ms_inner));
    println!("{}", f2(&s, &ms));
}

https://play.rust -lang.org /? Version = stable & mode = debug & edition = 2018 & gist =a32994fab2cabb6495807726f246da1e

0 голосов
/ 17 октября 2019

Структура кортежа

Кто-то уже любезно ответил с этим шаблоном нового типа. Я предоставляю шаблон для простого API и автоматической деконструкции. Запустите код на play.rust ...

struct SpecificStringType1(&'static str);
struct SpecificStringType2(&'static str);

fn do_something(x: SpecificStringType1, y: SpecificStringType2) {
    let val_1: String = x.to_string(); // No expensive cloning.
    let val_2: &str = y.as_str(); // No runtime penalty.

    println!("{}", val_1);
    println!("{}", val_2);
}

fn main() {
     let val_1 = SpecificStringType1("hello");
     let val_2 = SpecificStringType2("hello2");

     do_something(val_1, val_2);
     //do_something(val_1, val_1);
}

impl SpecificStringType1 {
    fn to_string(&self) -> String {
        let SpecificStringType1(value) = self;
        value.to_string()
    }

    fn as_str(&self) -> &str {
        let SpecificStringType1(value) = self;
        &value
    }
}

impl SpecificStringType2 {
    fn to_string(&self) -> String {
        let SpecificStringType2(value) = self;
        value.to_string()
    }

    fn as_str(&self) -> &str {
        let SpecificStringType2(value) = self;
        &value
    }
}

Черта FromStr

Запустите код на play.rust ...

use std::str::FromStr;
use std::fmt;

fn do_something(x: SpecificStringType1, y: SpecificStringType2) {
    let x_string = x.to_string();
    let y_string = y.to_string();

    println!("{}", x_string);
    println!("{}", y_string);
}


fn main() {
    let val_1: SpecificStringType1 = "hello".parse().unwrap();
    let val_2: SpecificStringType2 = "hello_2".parse().unwrap();
    let val_3: SpecificStringType2 = "hello_2".parse().unwrap();
    let val_4: SpecificStringType2 = "hello".parse().unwrap();

    do_something(val_1, val_2);
    //do_something(val_3, val_4);

    assert_eq!(val_3.to_string(), "hello_2".to_string());
    assert_eq!(&val_4.to_string(), "hello");
    //assert_eq!(
    //    "hello",
    //    "hello".parse::<SpecificStringType1>().unwrap()
    //)
}

#[derive(Debug, PartialEq)]
struct SpecificStringType1 {
    value: String
}

#[derive(Debug, PartialEq)]
struct SpecificStringType2 {
    value: String
}

impl FromStr for SpecificStringType1 {
    type Err = ();

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(SpecificStringType1 {
            value: s.to_string()
        })
    }
}

impl FromStr for SpecificStringType2 {
    type Err = ();

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(SpecificStringType2 {
            value: s.to_string()
        })
    }
}

impl fmt::Display for SpecificStringType1 {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let str = self.value.as_ref();
        fmt.write_str(str)
    }
}

impl fmt::Display for SpecificStringType2 {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let str = self.value.as_ref();
        fmt.write_str(str)
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...