Как преобразовать конкретное целое число в общее c целое число? - PullRequest
1 голос
/ 09 января 2020

У меня есть следующая функция:

use std::collections::HashMap;
use num::traits::Unsigned;

fn test<T: Unsigned>(strings: Vec<String>) -> HashMap<String, T>{
    let mut string_map: HashMap<String, T> = HashMap::new();
    for (i, string) in strings.iter().enumerate() {
        string_map.insert(string.clone(), i);
    }
    string_map
}

, которая вызывает следующую ошибку компиляции:

error[E0308]: mismatched types
  --> src/main.rs
   |
   |         string_map.insert(string.clone(), i);
   |                                           ^ expected type parameter, found usize
   |
   = note: expected type `T`
              found type `usize`
   = help: type parameters must be constrained to match other types
   = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters

Хотя черта Unsigned реализована только для целых типов без знака, компилятор не может распознать это. Как я могу привести i к T, так как i as T тоже не работает?

Ответы [ 3 ]

3 голосов
/ 09 января 2020

as выполняет только примитивные приведения между встроенными числовыми типами, но Unsigned может быть реализовано для ваших собственных типов и т. Д. c. Вот почему as не работает.

Вы можете использовать черту TryFrom:

use num::traits::Unsigned;
use std::collections::HashMap;
use std::convert::TryFrom;

fn test<T>(strings: Vec<String>) -> HashMap<String, T>
where
    T: Unsigned + TryFrom<usize>,
    <T as std::convert::TryFrom<usize>>::Error: Debug, // only for unwrap()
{
    let mut string_map: HashMap<String, T> = HashMap::new();
    for (i, string) in strings.iter().enumerate() {
        string_map.insert(string.clone(), T::try_from(i).unwrap());
    }
    string_map
}

fn main() {
    let result: HashMap<_, u16> = test(vec!["abc".into(), "def".into()]);
}

Теоретически вы можете использовать From вместо TryFrom, но в На практике большинство целочисленных типов, например u8, u16, u32, не гарантированно соответствуют usize. Так что TryFrom и обработка ошибок необходима.

2 голосов
/ 09 января 2020

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

use num::traits::Unsigned;
use std::collections::HashMap;

fn test(strings: Vec<String>) -> HashMap<String, impl Unsigned> {
    strings
        .into_iter()
        .enumerate()
        .map(|(i, s)| (s, i))
        .collect()
}
1 голос
/ 09 января 2020

Другой способ решения этой проблемы, вместо преобразования из usize в T, состоит в том, чтобы не использовать enumerate и вместо него связывать T так, чтобы он мог быть счетчиком.

use num::{Integer, Unsigned};
use std::collections::HashMap;

fn test<T: Clone + Unsigned + Integer>(strings: Vec<String>) -> HashMap<String, T> {
    let mut string_map: HashMap<String, T> = HashMap::new();
    let mut i = T::zero();
    for string in strings {
        string_map.insert(string, i.clone());
        i = i + T::one();
    }
    string_map
}

T имеет две новые границы:

  • Clone необходимо для того, чтобы иметь возможность вставить клон в карту и продолжить итерацию со счетчиком;
  • Integer позволяет использовать T::zero(), T::one() и сложение.

Имейте в виду, что поведение этого решения зависит от поведения переполнения T, тогда как ответ Джастины будет всегда pani c, если i выходит за пределы допустимого диапазона (или может быть переписано, чтобы вернуть Result, что может быть более желательным).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...