generics int rust traits

generics - ¿Cómo uso literales de números enteros cuando uso tipos genéricos?



int rust (2)

Quería implementar una función que calcule el número de dígitos dentro de cualquier tipo genérico de entero. Aquí está el código que se me ocurrió:

extern crate num; use num::Integer; fn int_length<T: Integer>(mut x: T) -> u8 { if x == 0 { return 1; } let mut length = 0u8; if x < 0 { length += 1; x = -x; } while x > 0 { x /= 10; length += 1; } length } fn main() { println!("{}", int_length(45)); println!("{}", int_length(-45)); }

Y aquí está la salida del compilador.

error[E0308]: mismatched types --> src/main.rs:5:13 | 5 | if x == 0 { | ^ expected type parameter, found integral variable | = note: expected type `T` found type `{integer}` error[E0308]: mismatched types --> src/main.rs:10:12 | 10 | if x < 0 { | ^ expected type parameter, found integral variable | = note: expected type `T` found type `{integer}` error: cannot apply unary operator `-` to type `T` --> src/main.rs:12:13 | 12 | x = -x; | ^^ error[E0308]: mismatched types --> src/main.rs:15:15 | 15 | while x > 0 { | ^ expected type parameter, found integral variable | = note: expected type `T` found type `{integer}` error[E0368]: binary assignment operation `/=` cannot be applied to type `T` --> src/main.rs:16:9 | 16 | x /= 10; | ^ cannot use `/=` on type `T`

Entiendo que el problema proviene de mi uso de constantes dentro de la función, pero no entiendo por qué la especificación de rasgos como Integer no resuelve esto.

La documentación de Integer dice que implementa los rasgos PartialOrd , etc. con Self (que supongo se refiere a Integer ). Al usar constantes enteras que también implementan el rasgo Integer , ¿no están definidas las operaciones y el compilador no debería compilar sin errores?

Intenté sufijar mis constantes con i32 , pero el mensaje de error es el mismo, reemplazando _ por i32 .


El problema es que el rasgo Integer puede ser implementado por cualquier cosa . Por ejemplo, ¡podría elegir implementarlo en su propia estructura! No habría forma de convertir el literal 0 o 1 a su estructura. Soy demasiado vago para mostrar un ejemplo de implementación, porque hay aproximadamente 10 métodos. ^ _ ^

Por eso existen Zero::zero y One::one . Puede (muy molestamente) crear todas las otras constantes a partir de llamadas repetidas a esas.

También puede usar los rasgos From y Into para convertir a su tipo genérico:

extern crate num; use num::Integer; use std::ops::{DivAssign, Neg}; fn int_length<T>(mut x: T) -> u8 where T: Integer + Neg<Output = T> + DivAssign, u8: Into<T>, { let zero = 0.into(); if x == zero { return 1; } let mut length = 0u8; if x < zero { length += 1; x = -x; } while x > zero { x /= 10.into(); length += 1; } length } fn main() { println!("{}", int_length(45)); println!("{}", int_length(-45)); }

Ver también:

  • ¿Cuál es la forma correcta de obtener literales cuando se usa el rasgo Float?

Muchas cosas van mal aquí:

  1. Como dice Shepmaster , 0 y 1 no se pueden convertir a todo lo que implementa Integer . Utilice Zero::zero y One::one lugar.
  2. 10 definitivamente no se puede convertir a nada que implemente Integer , debe usar NumCast para eso
  3. a /= b no es azúcar para a = a / b sino un rasgo separado que Integer no requiere.
  4. -x es una operación unaria que no es parte de Integer pero requiere el rasgo Neg (ya que solo tiene sentido para los tipos con signo).

Aquí hay una implementación. Tenga en cuenta que necesita un límite en Neg , para asegurarse de que da como resultado el mismo tipo que T

extern crate num; use num::{Integer, NumCast}; use std::ops::Neg; fn int_length<T>(mut x: T) -> u8 where T: Integer + Neg<Output = T> + NumCast, { if x == T::zero() { return 1; } let mut length = 0; if x < T::zero() { length += 1; x = -x; } while x > T::zero() { x = x / NumCast::from(10).unwrap(); length += 1; } length } fn main() { println!("{}", int_length(45)); println!("{}", int_length(-45)); }