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í:
-
Como dice Shepmaster
,
0
y1
no se pueden convertir a todo lo que implementaInteger
. UtiliceZero::zero
yOne::one
lugar. -
10
definitivamente no se puede convertir a nada que implementeInteger
, debe usarNumCast
para eso -
a /= b
no es azúcar paraa = a / b
sino un rasgo separado queInteger
no requiere. -
-x
es una operación unaria que no es parte deInteger
pero requiere el rasgoNeg
(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));
}