What is the Rust compiler flag to disable heap allocation? - rust

I was reading an article which says that Rust has a compiler option to disable heap allocation:
Rust has a number of standard library features that rely on the heap,
like boxes. However, Rust has compiler directives to completely
disable any heap-using language features, and statically verify that
none of these features are being used. It is entirely practical to
write a Rust program with no heap usage.
The ability to check for any mistaken heap allocations at compile-time would be very valuable to me. Exactly how do you do this in Rust? I don't see any relevant flags in the rustc man page.

There is no such compiler flag, although I don't think that's what the article meant:
Rust has compiler directives
I'd parse "directive" as an attribute, something like #[foo]. There's still no attribute that I'm aware of that accomplishes this goal.
Note that your article predates Rust 1.0:
Rust, version 0.11
The closest you can get is to avoid using the standard library and only using the core library. This eschews the use of liballoc, the primary mechanism for allocation. Doing this prevents types like Box or String from even existing, which is a pretty strong static guarantee.
#![no_std]
pub fn example() {
Box::new(42);
}
error[E0433]: failed to resolve. Use of undeclared type or module `Box`
--> src/lib.rs:4:5
|
4 | Box::new(42);
| ^^^ Use of undeclared type or module `Box`
However, nothing can stop you from rewriting the same code that is in liballoc and allocating memory yourself. You could also link against existing libraries that allocate memory. There's no magic compiler pass that detects heap allocation.
See also:
Is it possible to use Box with no_std?

Related

Why can a no_std crate depend on a crate that uses std?

In the example, hs reexport HashSet from std. But it compiles without error or warning. Why?
#![no_std]
pub use hs::HashSet;
pub fn new() -> HashSet<usize> {
HashSet::new()
}
pub fn insert(a: &mut HashSet<usize>, v: usize) {
a.insert(v);
}
Well #![no_std] just means that you don't include std by default. It doesn't mean you can't explicitly or implicitly (i.e. through other crates) still include std. In other words #![no_std] does not prohibit using std but it disables using it by default.
For example this works, too:
#![no_std]
extern crate std;
fn main() {
std::println!("hello!");
}
Every crate in Rust is opt-in, that is, you need to explicitly ask it as a dependency to have it as a dependency of your code (usually through Cargo.toml). All, but one: the standard library (hence the name), which is opt-out. That is to say, if you do nothing, Rust will implicitly understand you require it as a dependency, and you explicitly need to tell it that you don't (with #![no_std]) to prevent this. Besides this, std behaves like any other crate, that is, even if it's not one of your direct dependencies, you can still access to reexports of your actual dependencies.
It should be noted, however, that there is no reason to not want to standard library as a dependency except in situations where you just don't have it (for instance, if you are writing embedded code, or a kernel, or ...). In this case, having std as a (transitive) dependency will just fail at compile time, which means that not only you have to use #[no_std], but so should all the dependencies you have, and their dependencies, and so on... In your case, you have annotated your code with #[no_std], but there was no hard requirement for you to do so, so there is no problem in having a dependency that has access to std.
Finally, note that some parts of the standard library are accessible even if the whole standard library is not. For instance, core is not OS-dependent, so you can use it even on embedded, and usually obtaining alloc one way or another (that is, implementing what is required yourself for instance) also provides you quite a lot of the standard library. In particular, I think HashSet only requires core and alloc to be available, not the whole standard library.

Can Rust code compile without the standard library?

I'm currently in the progress of learning Rust. I'm mainly using The Rust Programming Language book and this nice reference which relates Rust features/syntax to C++ equivalents.
I'm having a hard time understanding where the core language stops and the standard library starts. I've encountered a lot of operators and/or traits which seems to have a special relationship with the compiler. For example, Rust has a trait (which from what I understand is like an interface) called Deref which let's a type implementing it be de-referenced using the * operator:
fn main() {
let x = 5;
let y = Box::new(x);
assert_eq!(5, x);
assert_eq!(5, *y);
}
Another example is the ? operator, which seems to depend on the Result and Option types.
Can code that uses those operators can be compiled without the standard library? And if not, what parts of the Rust language are depending on the standard library? Is it even possible to compile any Rust code without it?
The Rust standard library is in fact separated into three distinct crates:
core, which is the glue between the language and the standard library. All types, traits and functions required by the language are found in this crate. This includes operator traits (found in core::ops), the Future trait (used by async fn), and compiler intrinsics. The core crate does not have any dependencies, so you can always use it.
alloc, which contains types and traits related to or requiring dynamic memory allocation. This includes dynamically allocated types such as Box<T>, Vec<T> and String.
std, which contains the whole standard library, including things from core and alloc but also things with further requirements, such as file system access, networking, etc.
If your environment does not provide the functionality required by the std crate, you can choose to compile without it. If your environment also does not provide dynamic memory allocation, you can choose to compile without the alloc crate as well. This option is useful for targets such as embedded systems or writing operating systems, where you usually won't have all of the things that the standard library usually requires.
You can use the #![no_std] attribute in the root of your crate to tell the compiler to compile without the standard library (only core). Many libraries also usually support "no-std" compilation (e.g. base64 and futures), where functionality may be restricted but it will work when compiling without the std crate.
DISCLAIMER: This is likely not the answer you're looking for. Consider reading the other answers about no_std, if you're trying to solve a problem. I suggest you only read on, if you're interested in trivia about the inner workings of Rust.
If you really want full control over the environment you use, it is possible to use Rust without the core library using the no_core attribute.
If you decide to do so, you will run into some problems, because the compiler is integrated with some items defined in core.
This integration works by applying the #[lang = "..."] attribute to those items, making them so called "lang items".
If you use no_core, you'll have to define your own lang items for the parts of the language you'll actually use.
For more information I suggest the following blog posts, which go into more detail on the topic of lang items and no_core:
Rust Tidbits: What Is a Lang Item?
Oxidizing the technical interview
So yes, in theory it is possible to run Rust code without any sort of standard library and supplied types, but only if you then supply the required types yourself.
Also this is not stable and will likely never be stabilized and it is generally not a recommended way of using Rust.
When you're not using std, you rely on core, which is a subset of the std library which is always (?) available. This is what's called a no_std environment, which is commonly used for some types of "embedded" programming. You can find more about no_std in the Rust Embedded book, including some guidance on how to get started with no_std programming.

How to correctly use std::arch::_mm_loadu_si128 / _mm_storeu_si128

Usually one should be wary of transmuting (or casting) pointers to a higher alignment. Yet the interface to the above functions require *const _m128i and *mut _m128i pointers, respectively. Both are SIMD-aligned, which means I'd need to keep my arrays SIMD-aligned, too. On the other hand, the intrinsics are explicitly designed to load/store unaligned data.
Is this safe? Shouldn't we change the interface? Or at least document this fact?
I think this is a cross-language duplicate of Is `reinterpret_cast`ing between hardware vector pointer and the corresponding type an undefined behavior?.
As I explained over there, Intel defined the C/C++ intrinsics API such that loadu / storeu can safely dereference an under-aligned pointer, and that it's safe to create such pointers, even though it's UB in ISO C++ even to create under-aligned pointers. (Thus implementations that provide the intrinsics API must define the behaviour).
The Rust version should work identically. Implementations that provide it must make it safe to create under-aligned __m128i* pointers, as long as you don't dereference them "manually".
The other API-design option would be to have another version of the type that doesn't imply 16-byte alignment, like a __m128i_u or something. GNU C does this with their native vector syntax, but that's way off topic for Rust.

How to dynamically allocate a fixed runtime-sized array?

I'm essentially looking for malloc/free in Rust.
I found alloc::heap, but when I try to use it I get the following error:
main.rs:1:1: 1:19 error: unstable feature
main.rs:1 ![feature(alloc)]
note: this feature may not be used in the stable release channel
If you want to do it in high-level Rust (RAII, memory safety, bounds checking), you should allocate by creating a Vec. You can optionally use into_boxed_slice to disable resizing the container. See the book's FFI example (specifically the uncompress function) for an example of using Vec as an allocator.
Either use a nightly Rust or you will have to use libc::funcs::c95::stdlib::malloc(size: size_t) from the libc crate. You'll have to transmute the result of course.

Can libraries be distributed as a binary, so the end user cannot see the source code?

Is it possible to compile a Rust library crate so that the user can't see the source code but can still use the library?
If it is, are all the generics provided as "Source code" or some IR, or does Rust implement generics differently from C++ templates?
A lot of metadata is included with each library crate, be it statically linked (.rlib) or dynamically linked (.so/.dylib/.dll):
module structure
exported macro_rules macros
type and trait definitions
constants with their initializer expressions
signatures for all functions
the entire body of each function that is marked as #[inline] or is generic (default trait methods are considered generic over Self)
All of this is enough to reproduce some of the original source (how much depends on the usage of generics), albeit with no comments or other whitespace.
The function bodies are serialized in the compiler's internal AST structure - you can see a pretty form of it with rustc -Z ast-json lib.rs.
While the metadata is binary, not JSON, using librustc to extract all exported function definitions from a compiled crate, and pretty-printing the ASTs is fairly easy.
In the future, there might not be any AST past type-checking, so the metadata would encode an IR of sorts – one possibility is CFG, i.e. "control flow graph", which is already used internally in a couple places.
However, that would still expose more information than Java bytecode, it would be an optimization, you could still approximate the original code (and easily get something which compiles).
As such, there are only two options I can recommend:
expose a C API; it has the advantage of being a stable ABI, but it's quite limiting and brittle;
expose a Rust API using only trait objects, instead of generics; this way you get to keep memory safety and all monomorphic functions would still work normally, but trait objects (dynamic dispatch) cannot express all the patterns possible with generics: in particular, generic trait methods are not callable on trait objects (C++ should have a similar restriction for mixing template and virtual, with workarounds potentially available on a case-by-case basis).

Resources