Rust 1.52.0
blog.rust-lang.org
blog.rust-lang.org
That is really the sort of documentation I love, the one that are not just boring references but also learning you things. It’s really a great work.
It remembers me that, while I never had the chance to work with Django, more than ten years ago, its documentation literally taught me about how web applications, the concepts, the paradigms, and this probably is the root of my today career.
The people behind this documentation effort really deserve kudos.
https://github.com/rust-lang/rfcs/issues/2625
It's asking a lot, but it's one of very few weak points in Rust/C interoperability, and it would be great if it were addressed.
Of course, this would obviously be marked as unsafe code, and could only be used in very limited ways. But it would still be very useful for my project here:
https://github.com/jeff-davis/postgres-extension.rs
Which has code that handles setjmp/longjmp, and as far as I can tell it produces correct assembly, but it's currently undefined behavior as far as rust is concerned.
There's some related work here:
https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html
But it also seems to have slowed down.
But I agree that it's unfortunate that rust doesn't have good support for C++ exceptions unwinding through rust code.
These functions are of all different kinds with different numbers and types of arguments, so I'd need to wrap each one, or would need some pretty clever macros or code gen. Rust has really powerful macros (which I'm using currently), and I don't think it would work out as well in C. Also, it means a lot of symbol bloat, because each of these new C functions needs a symbol.
Also, it would mean that my crate would no longer be pure rust, so I'd have to figure out how to build all this C code on whatever platforms.
And rust is actually better at C interop than C++ in some important ways!
* Rust doesn't mess with struct definitions if they are declared #[repr(C)]. No extra vtable pointers. That means you can pass a struct back and forth between C and rust, and in the rust code you can do dynamic dispatch on that same C struct (no copying/conversion). That's amazing!
* Rust defines Option<&Foo> in a #[repr(C)] struct to be a plain pointer, where NULL means None. That means you can get type safety in rust working with nullable pointers you get from C, to make sure that your rust code checks for None. Again, no conversion or copying, you get this with ordinary nullable pointers coming from C.
> The most significant change in this release is not to the language or standard libraries, but rather an enhancement to tooling support for Clippy.
The following methods were stabilized.
Arguments::as_str
Peekable::next_if_eq
Peekable::next_if
char::MAX
char::REPLACEMENT_CHARACTER
char::UNICODE_VERSION
char::decode_utf16
char::from_digit
char::from_u32_unchecked
char::from_u32
slice::partition_point
str::rsplit_once
str::split_once
The following previously stable APIs are now const. char::len_utf8
char::len_utf16
char::to_ascii_uppercase
char::to_ascii_lowercase
char::eq_ignore_ascii_case
u8::to_ascii_uppercase
u8::to_ascii_lowercase
u8::eq_ignore_ascii_case
There you go. Enjoy!All we need now is better speech recognition for BonziBuddy.
One things I wonder about is why newly stabilized functions like char::from_u32 aren't const fn from the start. I get when existing functions have to be made const after the fact, but to me it feels that new functions should be stabilized already as const unless there is a good reason they can't be (e.g. they allocate).
EDIT: okay, I looked into it! (rust-analyzer is really useful for this...)
this function calls TryFrom::try_from. which runs afoul of https://github.com/rust-lang/rfcs/blob/master/text/0911-cons...
> Traits, trait implementations and their methods cannot be const - this allows us to properly design a constness/CTFE system that interacts well with traits - for more details, see Alternatives.
So, there you go.
#[inline]
const fn is_valid_scalar(c: u32) -> bool {
match c {
0..=0xD7FF => true,
0xD800..=0xDFFF => false, // surrogate pairs
0xE000..=0x10FFFF => true,
0x110000..=u32::MAX => false, // outside of Unicode range
}
}
But this could be reduced down to a simple if statement that tests for the invalid ranges. #[inline]
const fn is_valid_scalar(c: u32) -> bool {
matches!(c, 0..=0xD7FF) || matches!(c, 0xDE00..=0x10FFFF)
}
But I prefer your version for its clarity. It should compile into the same thing regardless.If you are working with a lot of data, you need to use some kind of hybrid cache-friendly memory/disk algorithm. For instance, to find the exact number of unique values, you might choose a hashing approach. If the hash table grows large and starts swapping, that will be incredibly slow. Instead, it's better to limit the hash table to a certain memory footprint, and when it grows any larger, you partition any new unique values you find and write them to disk. Then, you just count the members of the hash table, save the count, throw the hash table away, and read one of the partitions. Repeat until all the partitions are gone.
That requires a few important capabilities:
1. Track the memory usage of the hash table. It might be easy to estimate in simple cases, but when you have variable-width types that have their own pointers, etc., it gets very messy. It would be nice to just deep copy values into the hash table's memory pool, and then ask how big the memory pool is now. If it's over the limit, don't accept new unique values, just send them to the disk partitions.
2. Free the heap all at once and release all the memory when you are done with the current batch.
These things are not easy in C, but straightforward if you have a custom allocator that uses its own regions of memory. But in rust, it's pretty hard to do this in a generic way.
You can already set a custom global allocator, but soon you'll be able to specify allocators per collection.
I found this: https://doc.rust-lang.org/std/alloc/trait.Allocator.html
but it doesn't look like it allows inspecting the size. I'd also like to know how it works to "move a value in" to a collection, does it actually do a memcpy into the collection's heap?
Also, is it really a separate instance of the allocator for the collection? That is, can I wipe all the memory for one hash table in O(1) without touching another hash table that uses the same allocator?
EDIT: It would also be nice if there was a way to gracefully handle OOM. Maybe if you could specify a maximum size for some heap, it could be a good way to test how your code fails on OOM (which can still happen in a lot of cases with some kernel configs).
When you call `Vec::push` or `HashMap::insert`, it'll allocate on the heap if there isn't already capacity for it in the already allocated space. If the custom allocator you make just allocates one large space from the global allocator, then you can deallocate the whole thing at once in the `Drop` implementation.
There is limited documentation at the moment because the feature is still in flux, but take a look at `Vec::new_in` [0] to get an idea of how a collection can use an allocator.
[0]: https://doc.rust-lang.org/std/vec/struct.Vec.html#method.new...