ps4 - rust traduccion
¿Cómo declarar bitflags tipeados en Rust? (4)
Debería saber que ese type
crea un alias de tipo en Rust, no un tipo nuevo, por lo que MyFlag
y MyOtherFlag
tienen el mismo tipo.
Si estas banderas son nombradas pero no indexadas, y no son muy numerosas, entonces podrías incluir un montón de tipos de bool
en una estructura.
#[repr(packed)]
struct MyFlags {
a: bool,
b: bool
}
De hecho, cada bool
requiere un u8
incluso con #[repr(packed)]
. No sé si eso se origina con referencias de apoyo a bool
individuales, pero también toman un u8
sin #[repr(packed)]
, así que no estoy seguro. Pensaría que un RFC o un problema se podría archivar sobre eso aunque dé 1240 . Si desperdicia un u8
por indicador como este funciona, entonces es probable que la sintaxis sea compatible con los campos de bits cuando aterricen.
Si necesita indizar en los indicadores, entonces también necesitaría una solución desordenada o sofisticada en C.
Si desea bitfields con valores mayores que bool, hay una variedad de maneras de hackear esto junto con los dos comentarios anteriores. Y algunas cajas bitfield. Encontrará varios más discutidos en los hilos de discusión de Rust RFC 314 y 1449 sobre la adición de soporte de bitfield a Rust. En este caso, lo haría como quiera por ahora, pero tal vez planee cambiarlo a bitfields cada vez que aterricen.
Es posible declarar banderas en Rust, de manera similar a como se haría en C.
pub const FOO: u32 = (1 << 0);
pub const BAR: u32 = (1 << 1);
let flag: u32 = (FOO | BAR);
Esto funciona bien, sin embargo no es seguro, lo que hace que sea posible confundir accidentalmente el uso de la bandera.
¿Es posible definir un tipo que se pueda usar para evitar el uso accidental de marcas no válidas?
Por ejemplo:
pub type MyOtherFlag = u32;
pub type MyFlag = u32;
pub const FOO: MyFlag = (1 << 0);
pub const BAR: MyFlag = (1 << 1);
let flag: MyOtherFlag = (FOO | BAR);
// ^^^^^^^^^^^ I''d like this to raise a type error to avoid
// confusion between MyOtherFlag and MyFlag.
// Currently it doesn''t since
// type aliases aren''t seen as distinct types.
... donde mezclar en otros tipos de banderas generará un error?
¿Se puede hacer esto con el sistema de tipos de Rust sin la sobrecarga de definir una gran cantidad de componentes internos complejos? Específicamente, quiero decir macros grandes o tipos que necesitan implementar operadores binarios. La caja bitflags tiene más de 300 líneas de código, por ejemplo.
Soy consciente del bitflags crate, pero me gustaría saber si esto se puede lograr con el sistema de tipos de Rust, sin tener que implementar operadores que ya estén disponibles para el tipo subyacente.
Podrías (no sé si es de algún modo idiomático) simplemente usar las enumeraciones de Rust:
pub enum MyFlags {
Meaning1,
Meaning2,
Meaning3,
...,
MeaningX
}
De esta manera tienes un significado claro para tus banderas. Una vez hecho esto, puede escribir algunas funciones auxiliares alrededor de esta enumeración para la conversión de Rust-to-C.
fn to_u32(flag: &MyFlags) -> u32 {
match flag {
&MyFlags::Meaning1 => return (1 << 0),
&MyFlags::Meaning2 => return (1 << 1),
&MyFlags::Meaning3 => return (1 << 2),
&MyFlags::MeaningX => return (1 << 3),
}
}
fn to_bitflags_flags(flags: &Vec<MyFlags>) -> u32 {
let mut bitflags = 0u32;
for flag in flags {
bitflags |= to_u32(flag);
}
return bitflags;
}
Hay una colección EnumSet
inestable en la biblioteca estándar que funciona con el rasgo CLike
también inestable. Funciona de esta manera: usted define una enumeración, cuyos miembros toman un número de bit (¡ no una máscara!) Como su valor, y EnumSet
usa el bit en la posición designada por el valor enum para almacenar si el miembro enum es parte del conjunto o no. En tiempo de ejecución, un EnumSet
está representado por un solo usize
. EnumSet
está parametrizado en el tipo de enumeración, por lo que los conjuntos basados en diferentes enumeraciones no tendrán el mismo tipo.
#![feature(collections)]
#![feature(enumset)]
extern crate collections;
use collections::enum_set::{CLike, EnumSet};
use std::mem;
#[derive(Clone, Copy, Debug)]
#[repr(usize)]
enum MyFlag {
Foo,
Bar,
}
impl CLike for MyFlag {
fn to_usize(&self) -> usize {
*self as usize
}
fn from_usize(v: usize) -> MyFlag {
unsafe { mem::transmute(v) }
}
}
fn main() {
let mut flags = EnumSet::new();
flags.insert(MyFlag::Foo);
flags.insert(MyFlag::Bar);
println!("{:?}", flags);
}
Contabilización de respuesta que utiliza una macro como una posible solución a la pregunta.
Ejemplo de uso:
struct_bitflag_impl!(pub struct MyFlag(pub u8));
pub struct MyFlag(u8);
struct_bitflag_impl!(MyFlag);
pub struct MyOtherFlag(u32);
struct_bitflag_impl!(MyOtherFlag);
- Tipo seguro
- Cero sobrecarga en comparación con los tipos de entero simple.
- El valor subyacente es accesible desde
value.0
si es necesario. - Utiliza una única macro:
struct_bitflag_impl
que puede reutilizarse y aplicarse a múltiples tipos de estructuras.
Cada declaración es solo 2 líneas.
La macro:
/// Implements bitflag operators for integer struct, eg:
/// ```
/// pub struct MyFlag(u8);
/// struct_bitflag_impl!(MyFlag);
/// ```
macro_rules! struct_bitflag_impl {
($p:ident) => {
// Possible additions:
// * left/right shift.
// * Deref to forward methods to the underlying type.
impl ::std::ops::BitAnd for $p {
type Output = $p;
fn bitand(self, _rhs: $p) -> $p { $p(self.0 & _rhs.0) }
}
impl ::std::ops::BitOr for $p {
type Output = $p;
fn bitor(self, _rhs: $p) -> $p { $p(self.0 | _rhs.0) }
}
impl ::std::ops::BitXor for $p {
type Output = $p;
fn bitxor(self, _rhs: $p) -> $p { $p(self.0 ^ _rhs.0) }
}
impl ::std::ops::Not for $p {
type Output = $p;
fn not(self) -> $p { $p(!self.0) }
}
impl ::std::ops::BitAndAssign for $p {
fn bitand_assign(&mut self, _rhs: $p) { self.0 &= _rhs.0; }
}
impl ::std::ops::BitOrAssign for $p {
fn bitor_assign(&mut self, _rhs: $p) { self.0 |= _rhs.0; }
}
impl ::std::ops::BitXorAssign for $p {
fn bitxor_assign(&mut self, _rhs: $p) { self.0 ^= _rhs.0; }
}
// Other operations needed to be generally usable.
impl PartialEq for $p {
fn eq(&self, other: &$p) -> bool { self.0 == other.0 }
}
impl Copy for $p { }
impl Clone for $p {
fn clone(&self) -> $p { $p(self.0) }
}
}
}
Para una variación alternativa en esta macro que admite derive
que se necesita para que se puedan usar constantes de este tipo en una declaración de match
, se puede escribir.
Esto también evita tener que definir Copiar y Clonar.
struct_bitflag_impl!(pub struct MyFlag(pub u8));
La macro:
macro_rules! struct_bitflag_impl {
// pub/pub
(pub struct $name:ident ( pub $t:tt ) ) => {
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub struct $name(pub $t);
_struct_bitflag_gen_impls!($name, $t);
};
// private/pub
(struct $name:ident ( pub $t:tt ) ) => {
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
struct $name(pub $t);
_struct_bitflag_gen_impls!($name, $t);
};
// pub/private
(pub struct $name:ident ( $t:tt ) ) => {
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
struct $name($t);
_struct_bitflag_gen_impls!($name, $t);
};
// private/private
(struct $name:ident ( $t:tt ) ) => {
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
struct $name($t);
_struct_bitflag_gen_impls!($name, $t);
}
}
macro_rules! _struct_bitflag_gen_impls {
($t:ident, $t_base:ident) => {
impl ::std::ops::BitAnd for $t {
type Output = $t;
#[inline]
fn bitand(self, _rhs: $t) -> $t { $t(self.0 & _rhs.0) }
}
impl ::std::ops::BitOr for $t {
type Output = $t;
#[inline]
fn bitor(self, _rhs: $t) -> $t { $t(self.0 | _rhs.0) }
}
impl ::std::ops::BitXor for $t {
type Output = $t;
#[inline]
fn bitxor(self, _rhs: $t) -> $t { $t(self.0 ^ _rhs.0) }
}
impl ::std::ops::Not for $t {
type Output = $t;
#[inline]
fn not(self) -> $t { $t(!self.0) }
}
impl ::std::ops::BitAndAssign for $t {
#[inline]
fn bitand_assign(&mut self, _rhs: $t) { self.0 &= _rhs.0; }
}
impl ::std::ops::BitOrAssign for $t {
#[inline]
fn bitor_assign(&mut self, _rhs: $t) { self.0 |= _rhs.0; }
}
impl ::std::ops::BitXorAssign for $t {
#[inline]
fn bitxor_assign(&mut self, _rhs: $t) { self.0 ^= _rhs.0; }
}
/// Support for comparing with the base type, allows comparison with 0.
///
/// This is used in typical expressions, eg: `if (a & FLAG) != 0 { ... }`
/// Having to use MyFlag(0) all over is too inconvenient.
impl PartialEq<$t_base> for $t {
#[inline]
fn eq(&self, other: &$t_base) -> bool { self.0 == *other }
}
}
}