I want to be able to use Option<NonZeroU8> for its compact repr, but still be able to use 0.
Is there a way to use u8::MAX as the non-representable value instead of 0?
To truly get the niche optimization directly, you'd have to use the super unstable rustc_ attributes.
#![feature(rustc_attrs)]
#[rustc_layout_scalar_valid_range_start(0)]
#[rustc_layout_scalar_valid_range_end(254)]
struct NonMaxU8(u8);
fn main() {
dbg!(std::mem::size_of::<Option<NonMaxU8>>());
}
[src/main.rs:8] std::mem::size_of::<Option<NonMaxU8>>() = 1
I say "super unstable" because these attributes will never be stabilized, at least not in their current form. Using them without feature(rustc_attrs) will emit the following message:
error[E0658]: the `#[rustc_layout_scalar_valid_range_start]` attribute is just used to enable niche optimizations in libcore and libstd and will never be stable
--> src/main.rs:3:1
|
3 | #[rustc_layout_scalar_valid_range_start(0)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: add `#![feature(rustc_attrs)]` to the crate attributes to enable
error[E0658]: the `#[rustc_layout_scalar_valid_range_end]` attribute is just used to enable niche optimizations in libcore and libstd and will never be stable
--> src/main.rs:4:1
|
4 | #[rustc_layout_scalar_valid_range_end(254)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: add `#![feature(rustc_attrs)]` to the crate attributes to enable
There have been asks for compiler support for ranged integers, like the discussion here, but I don't think anything has been formally proposed as of yet.
There is the nonmax crate that provides this. See NonMaxU8 specifically. This works because it uses NonZeroU8 internally and does the appropriate conversion upon creation and the .get() method.
If you wanted a different variant, not just zero or max, you can easily make your own. The nonmax crate uses an XOR strategy to zero-out the non-representable value or use a modular arithmetic method like is suggested in the comments:
pub struct NonTenU8(NonZeroU8);
impl NonTenU8 {
const UNREPRESENTABLE: u8 = 10;
/// Creates a u8 value that cannot be ten.
pub fn new(value: u8) -> Option<Self> {
NonZeroU8::new(value ^ Self::UNREPRESENTABLE).map(Self)
// NonZeroU8::new(value.wrapping_sub(Self::UNREPRESENTABLE)).map(Self)
}
pub fn get(&self) -> u8 {
self.0.get() ^ Self::UNREPRESENTABLE
// self.0.get().wrapping_add(Self::UNREPRESENTABLE)
}
}
Related
I use https://github.com/dtolnay/clang-ast to parse the JSON produced by Clang representing an AST to be available as a Rust data type, specifically the Node. I'd like to insert the nodes from the tree (Node<T> is recursive structure) into a HashSet. I could not even insert the root note:
use std::collections::HashSet;
use log::debug;
use std::env;
use serde::Deserialize;
pub type Node = clang_ast::Node<Clang>;
#[derive(Deserialize)]
#[derive(Debug)]
pub enum Clang {
BinaryOperator(BinaryOperator),
Other,
}
#[derive(Deserialize, Debug)]
pub struct BinaryOperator {
pub opcode: String,
pub range: clang_ast::SourceRange,
}
fn main() {
env_logger::init();
let json = std::fs::read_to_string("ast.json").unwrap();
let node :Node = serde_json::from_str(&json).unwrap();
let mut node_set = HashSet::new();
node_set.insert(node);
}
this fails the compilation with:
error[E0277]: the trait bound `clang_ast::Node<Clang>: Eq` is not satisfied
--> src/main.rs:28:21
|
28 | node_set.insert(node);
| ------ ^^^^ the trait `Eq` is not implemented for `clang_ast::Node<Clang>`
| |
| required by a bound introduced by this call
|
note: required by a bound in `HashSet::<T, S>::insert`
...
So, I tried to add the Eq and PartialEq implementations using (following some advice from How to implement Eq and Hash for my own structs to use them as a HashMap key?):
impl PartialEq for Node {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl Eq for Node {}
however this fails with:
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
--> src/main.rs:8:1
|
8 | impl PartialEq for Node {
| ^^^^^---------^^^^^----
| | | |
| | | `clang_ast::Node` is not defined in the current crate
| | `clang_ast::Node` is not defined in the current crate
| impl doesn't use only types from inside the current crate
|
= note: define and implement a trait or new type instead
How do I make this work ? Also, why does the language/compiler imposes such limit ?
Following the answers on Implement foreign trait for foreign type does not really help because there are more pieces to the puzzle (implementing Eq, PartialEq, the Hash trait, dealing with the recursive nature of clang_ast::Note<T>).
How do I make this work ?
Define a newtype wrapper e.g. struct Node(clang:ast::Node<Clang>).
, or implement the trait on clang_ast::Node<Clang> rather than just clang_ast::Node1.
Also, why does the language/compiler imposes such limit ?
Coherence. There should be only one implementation of a trait for a struct (ignoring specialisation). Allowing crates to implement traits they didn't define on types they didn't define is an opportunity for conflicts and incoherences (where one dependency uses one implementation and an other library uses an other incompatible implementation) which could lead to unsafety if they interact with the same objects in different ways (e.g. a generic collection of some sort, like a hashset).
Here though per the implementation coherence rules since Clang is a local type as long as there are no uncovered types appearing as one of its parameters it should be fine (I could not test this on the playground as clang_ast is to minor to figure there, but it works fine with Box).
In other contexts (like Box) overlapping can also be an issue, but here clang_ast::Node does not implement PartialEq at all, not even conditionally. Until that happens there's no issue.
[1]: incidentally you may want to report that the error message is misleading, since Node is a generic type I don't think it makes sense that Rust accepts this attempt. In fact I can not reproduce it, on the playground trying to impl PartialEq for Box fails with "error[E0107]: missing generics for struct Box".
edit: struct out the completely incorrect bits where I thought the coherence rules were less strict than they actually are (which is only the case for Box, Pin, and reference types).
I am writing an OS in Rust and need to directly call into a virtual address that I'm calculating (of type u32). I expected this to be relatively simple:
let code = virtual_address as (extern "C" fn ());
(code)();
However, this complains that the cast is non-primitive. It suggests I use the From trait, but I don't see how this could help (although I am relatively new to Rust and so could be missing something).
error[E0605]: non-primitive cast: `u32` as `extern "C" fn()`
--> src/main.rs:3:16
|
3 | let code = virtual_address as (extern "C" fn ());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
I have everything in libcore at my disposal, but haven't ported std and so can't rely on anything that isn't no_std
Casts of the type _ as f-ptr are not allowed (see the Rustonomicon chapter on casts). So, as far as I can tell, the only way to cast to function pointer types is to use the all mighty weapon mem::transmute().
But before we can use transmute(), we have to bring our input into the right memory layout. We do this by casting to *const () (a void pointer). Afterwards we can use transmute() to get what we want:
let ptr = virtual_address as *const ();
let code: extern "C" fn() = unsafe { std::mem::transmute(ptr) };
(code)();
If you find yourself doing this frequently, various kinds of macros can remove the boilerplate. One possibility:
macro_rules! example {
($address:expr, $t:ty) => {
std::mem::transmute::<*const (), $t>($address as _)
};
}
let f = unsafe { example!(virtual_address, extern "C" fn()) };
f();
However, a few notes on this:
If you, future reader, want to use this to do simple FFI things: please take a moment to think about it again. Calculating function pointers yourself is rarely necessary.
Usually extern "C" functions have the type unsafe extern "C" fn(). This means that those functions are unsafe to call. You should probably add the unsafe to your function.
Please consider the following minimal example in Rust:
const FOOBAR: usize = 3;
trait Foo {
const BAR: usize;
}
struct Fubar();
impl Foo for Fubar {
const BAR: usize = 3;
}
struct Baz<T>(T);
trait Qux {
fn print_bar();
}
impl<T: Foo> Qux for Baz<T> {
fn print_bar() {
println!("bar: {}", T::BAR); // works
println!("{:?}", [T::BAR; 3]); // works
println!("{:?}", [1; FOOBAR]); // works
println!("{:?}", [1; T::BAR]); // this gives an error
}
}
fn main() {
Baz::<Fubar>::print_bar();
}
The compiler gives the following error:
error[E0599]: no associated item named `BAR` found for type `T` in the current scope
--> src/main.rs:24:30
|
24 | println!("{:?}", [1; T::BAR]); // this gives an error
| ^^^^^^ associated item not found in `T`
|
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `BAR`, perhaps you need to implement it:
candidate #1: `Foo`
Whatever the answer to my question, this is not a particularly good error message because it suggests that T does implement Foo despite the latter being a trait bound. Only after burning a lot of time did it occur to me that in fact T::BAR is a perfectly valid expression in other contexts, just not as a length parameter to an array.
What are the rules that govern what kind of expressions can go there? Because arrays are Sized, I completely understand that the length are to be known at compile time. Coming from C++ myself, I would expect some restriction akin to constexpr but I have not come across that in the documentation where it just says
A fixed-size array, denoted [T; N], for the element type, T, and the non-negative compile-time constant size, N.
As of Rust 1.24.1, the array length basically needs to either be a numeric literal or a "regular" constant that is a usize. There's a small amount of constant evaluation that exists today, but it's more-or-less limited to basic math.
a perfectly valid expression in other contexts, just not as a length parameter to an array
Array lengths don't support generic parameters. (#43408)
this is not a particularly good error message
Error message should be improved for associated consts in array lengths (#44168)
I would expect some restriction akin to constexpr
This is essentially the restriction, the problem is that what is allowed to be used in a const is highly restricted at the moment. Notably, these aren't allowed:
functions (except to construct enums or structs)
loops
multiple statements / blocks
Work on good constant / compile-time evaluation is still ongoing. There are a large amount of RFCs, issues, and PRs improving this. A sample:
Const fn tracking issue (RFC 911)
Allow locals and destructuring in const fn (RFC 2341)
Allow if and match in constants (RFC 2342)
I have an enum:
enum Numbers {
A = 1,
}
How can I overload the operator so that I can compare the Numbers enum with another standard number without have to do as u32? If I want to do:
let a = Numbers::A < 4
I think i read somewhere that it's possible to do with some kind of macros? Maybe there is a crate that lets me do this? So far the only thing I have had success with is as u32 but it gets quite tedious to have to write it out every time.
You do it the exact same way that you overload an operator for a struct: implement the appropriate std::ops trait.
However, you don't really want to overload the meaning of <, you still wish to perform a comparison.
If you run the code you typed, the compiler tells you what to do:
error[E0369]: binary operation `<` cannot be applied to type `Numbers`
--> src/main.rs:6:13
|
6 | let a = Numbers::A < 4;
| ^^^^^^^^^^^^^^
|
= note: an implementation of `std::cmp::PartialOrd` might be missing for `Numbers`
So, implement PartialOrd:
#[derive(Copy, Clone)]
enum Numbers {
A = 1,
}
use std::cmp::Ordering;
impl PartialEq<i32> for Numbers {
fn eq(&self, other: &i32) -> bool {
(*self as i32).eq(other)
}
}
impl PartialOrd<i32> for Numbers {
fn partial_cmp(&self, other: &i32) -> Option<Ordering> {
(*self as i32).partial_cmp(other)
}
}
fn main() {
let a = Numbers::A < 4;
}
As Sven Marnach points out:
It may be worth pointing out that this implementation only allows comparisons with Numbers on the left-hand side and i32 on the right-hand side. Comparisons like 4 > Numbers::A and Numbers.A < Numbers.B would require separate implementations. In addition, since you are dealing with integers, you would also want to implement Ord and Eq, so for all combinations of operands you end up with twelve trait implementations.
Of course, it depends on your exact cases:
if you want to compare Numbers to Numbers, you might be able to #[derive(PartialOrd)].
if you want Eq, you can derive it.
you can write macros to reduce some of the redundancy.
See also:
How do I convert an enum reference to a number?
How do I implement the Add trait for a reference to a struct?
This simple program:
fn main() {
let b: Box<i32> = Box::new(1);
b.into_raw();
}
Produces this inconvenient error when compiled with Rust 1.12.0:
error: no method named `into_raw` found for type `Box<i32>` in the current scope
--> <anon>:3:7
|
3 | b.into_raw();
| ^^^^^^^^
|
= note: found the following associated functions; to be used as methods, functions must have a `self` parameter
= note: candidate #1 is defined in an impl for the type `Box<_>`
This is because into_raw is not defined to take self as parameter, but instead is defined as:
impl Box<T: ?Sized> {
fn into_raw(b: Box<T>) -> *mut T;
}
This seems inconvenient, and I cannot find a rationale.
So... why?
Because 99.995% of the time (statistic totally made up), you expect method calls to happen to the thing being pointed to, not to the pointer itself. As a result, the "smart pointer" types in Rust generally avoid doing anything to break that expectation. An obvious exception would be something like Rc/Arc implementing Clone directly.
Box implements Deref, which means that all methods that are enclosed by the Box are automatically made available; from the outside, Box<T> and T look and act the same.
If into_raw were a method instead of an associated function, it would shadow any into_raw method on the contained type.
There are other examples of these enhancing associated functions on Rc, such as downgrade or try_unwrap, or on Arc, such as make_mut.