Giter Site home page Giter Site logo

nomicon's Introduction

The Rustonomicon

The Dark Arts of Advanced and Unsafe Rust Programming

Nicknamed "the Nomicon."

NOTE: This is a draft document, and may contain serious errors

Instead of the programs I had hoped for, there came only a shuddering blackness and ineffable loneliness; and I saw at last a fearful truth which no one had ever dared to breathe before โ€” the unwhisperable secret of secrets โ€” The fact that this language of stone and stridor is not a sentient perpetuation of Rust as London is of Old London and Paris of Old Paris, but that it is in fact quite unsafe, its sprawling body imperfectly embalmed and infested with queer animate things which have nothing to do with it as it was in compilation.

This book digs into all the awful details that are necessary to understand in order to write correct Unsafe Rust programs. Due to the nature of this problem, it may lead to unleashing untold horrors that shatter your psyche into a billion infinitesimal fragments of despair.

Requirements

Building the Nomicon requires mdBook. To get it:

cargo install mdbook

mdbook usage

To build the Nomicon use the build sub-command:

mdbook build

The output will be placed in the book subdirectory. To check it out, open the index.html file in your web browser. You can pass the --open flag to mdbook build and it'll open the index page in your default browser (if the process is successful) just like with cargo doc --open:

mdbook build --open

There is also a test sub-command to test all code samples contained in the book:

mdbook test

linkcheck

We use the linkcheck tool to find broken links. To run it locally:

curl -sSLo linkcheck.sh https://raw.githubusercontent.com/rust-lang/rust/master/src/tools/linkchecker/linkcheck.sh
sh linkcheck.sh --all nomicon

Contributing

Given that the Nomicon is still in a draft state, we'd love your help! Please feel free to open issues about anything, and send in PRs for things you'd like to fix or change. If your change is large, please open an issue first, so we can make sure that it's something we'd accept before you go through the work of getting a PR together.

nomicon's People

Contributors

alexcrichton avatar apasel422 avatar arthur-milchior avatar bennyyip avatar billxwb avatar bluss avatar camelid avatar centril avatar conradludgate avatar ehuss avatar faern avatar gankra avatar izderadicka avatar japaric avatar joe1994 avatar johntitor avatar killercup avatar lokathor avatar manishearth avatar mark-simulacrum avatar mdaverde avatar purewhitewu avatar ralfjung avatar rufflewind avatar simonsapin avatar steveklabnik avatar tesuji avatar thepuzzlemaker avatar tshepang avatar vorner avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

nomicon's Issues

Section on coercions fails to mention requirement on trait object creation

Rust converts a Pointer<T> to a Pointer<Trait> where T implements Trait and Pointer is a type constructor like & or Box, only if Pointer<T> is a thin pointer. The Nomicon suggests otherwise.

According to the "Coercions" section of the Nomicon:

  • Pointer<T>: CoerceUnsized<Pointer<U>> for all pointer types, where T: Unsize<U>.
  • Unsize<U> is automatically implemented for T if T: U.

Suppose I have two traits T1 and T2, and impl T2 for T1. Since T1: Unsize<T2>, the Nomicon claims that Pointer<T1>: CoerceUnsized<Pointer<T2>>. But I can't convert a Box<T1> to a Box<T2>; that would be a doubly-fat pointer, since it needs both the vtable for impl T2 for T1 and whatever vtable Box<T1> was using.

There was a longish thread about this; I think this post is where I finally got it right: https://users.rust-lang.org/t/trait-objects-of-trait-objects/14077/27

Variance: anyway typo

that lifetime information isn't shared in anyway

Should be "that lifetime information isn't shared in any way"

In chapter 3.2, `Why Aliasing Matters` section, is the optimize example wrong?

In chapter 3.2(Aliasing), Why Aliasing Matters section, is the optimize example wrong?

fn compute(input: &u32, output: &mut u32) {
    let cached_input = *input; // keep *input in a register
    if cached_input > 10 {
        *output = 2;  // x > 10 implies x > 5, so double and exit immediately
    } else if cached_input > 5 {
        *output *= 2;
    }
}

should be:

fn compute(input: &u32, output: &mut u32) {
    let cached_input = *input; // keep *input in a register
    if cached_input > 10 {
        *output *= 2;  // x > 10 implies x > 5, so double and exit immediately
    } else if cached_input > 5 {
        *output *= 2;
    }
}

or

fn compute(input: &u32, output: &mut u32) {
    let cached_input = *input; // keep *input in a register
    if cached_input > 10 {
        *output = 1;  // x > 10 implies x > 5, so double and exit immediately
    } else if cached_input > 5 {
        *output *= 2;
    }
}

Plans for editions

Hello

I've started to edit the lifetimes chapter because NLL is coming with the 2018 edition and there are some differences (and some โ€žshould not compileโ€œ examples actually compile under that edition). Then I noticed that even nightly still doesn't use NLL on the 2015 edition and my new examples fail the mdbook test (and I haven't found a way to enable the 2018 edition in mdbook).

My question is what should nomicon do about editions. Should it mostly aim for the latest one and optionally point out differences to previous editions? Should it start getting โ€žportedโ€œ as soon as possible? If so, what about the failing examples? Or should I postpone the migration and just make sure that one example (https://doc.rust-lang.org/nightly/nomicon/lifetime-mismatch.html) successfully fails to compile?

Expand lifetimes section

Hi,

I'd like to expand the lifetimes section of the rustonomicon with the example (and Lukas's answer) to the question in this stack overflow post.

If you'd welcome this change I will do the legwork and raise the PR, but I want to check that such a change is welcomed before investing the time.

(I'm a Rust beginner, and a first time contributor to rust docs).

Thanks

Explain safety rules for passing pointers to foreign functions

The usual rule in Rust is that creating a pointer is safe but dereferencing it is not, so people may just assume that functions that take a pointer need not be made unsafe unless they dereference it. For example:

Indeed, if a function stashes a pointer, usually you don't mark it unsafe, because you instead apply the above rules to the functions that dereference the pointer. But if needed you can mark it unsafe (Shared::into_owned() is another example from crossbeam, but also more simply Box::from_raw).

This is obviously a possibility if you think of safety in terms of contracts, and it becomes particularly important for FFI: when creating a safe interface for a library that accepts pointer, particular care must be made in getting lifetimes correct. The case of a foreign function stashing a pointer happens relatively often in the case of FFI, and when that happens you have to consider whether all future foreign accesses to the pointer will happen safely. If not, the Rust method should be declared unsafe even though it only creates a pointer and does not dereference it; failure to do so will result in an API that cannot guarantee Rust's safety, so perhaps it should be pointed out in the FFI section.

Subtyping: function handling 'long and 'short

if we need a function that can handle anything that lives for at least 'long, it's perfectly fine for it to be able to handle anything that lives for at least 'short.

That is confusing. The way I understand this sentence is if we have fn foo(arg: &'long) that should be able to handle foo(&'short val), which doesn't sound right.

I think it's in the context of "But if we flip it around, โ€ฆ" earlier in the paragraph, so I'm not sure if I'm supposed to flip 'long and 'short here.

Update/rewrite "Calling Rust code from C" section

This https://doc.rust-lang.org/nomicon/ffi.html#calling-rust-code-from-c
( and even in the book here: https://doc.rust-lang.org/stable/book/ch19-01-unsafe-rust.html#calling-rust-functions-from-other-languages )

has no mention of all the other info needed to accomplish this, such as: compile as a cdylib by adding crate-type = [ "cdylib" ] in Cargo.toml [lib] section, or using cbindgen to generate a header file, or the gcc command line to compile the .cpp file.

(NOTE: the below link is now invalid as the author deleted their account.)
I've attempted all the needed stuff in a working example here.

repr(Rust) page includes a list item where it shouldn't

The published nomicon renders the following

Alignment is at least 1, and always a power of
2. Most primitives are generally aligned to their size, although this is platform-specific behavior. In particular, on x86 u64 and f64 may be only aligned to 32 bits.

as


Alignment is at least 1, and always a power of

  1. Most primitives are generally aligned to their size, although this is platform-specific behavior. In particular, on x86 u64 and f64 may be only aligned to 32 bits.

which can be seen here.

I think this is a bug in mdbook.

Is 0 alloc UB?

I've been playing with 0 allocs (After reading https://doc.rust-lang.org/stable/nomicon/exotic-sizes.html#zero-sized-types-zsts I was expecting it to give me null pointers) but I kept getting seg faults. Asking in the rust discord, someone told me 0 allocs are UB (And they only may return null pointers). If so, I think clarifying that they're UB a bit more might be a good idea.

I'm having a great time reading the book by the way, great job ๐Ÿ‘

Rework drop check stuff, it's probably outdated

I don't really understand or care about the new rules (they mostly just plug super-degenerate-you'll-probably-never-see-this holes in the language, afaict), but they're different and any discussion of dropcheck is probably wrong now.

CC @pnkfelix

Publish, translate and print?

In rust-lang/book#535, @3442853561 raised these questions:

Is it time to publish(print, translate) The Rustonomicon ?

And now that this is its own repo, I think this is a better place for this issue!

Presumably, translations would be blocked on mdBook getting translation support: azerupi/mdBook#5

I don't know if a publisher would be interested in the nomicon and how to go about that.

Explain UnsafeCell and Interior Mutability

It seems to be a reoccurring pattern that people are confused about UnsafeCell and interior mutability, what it does, when they need it and when they don't. And this kind of stuff fits squarely in the scope of the Nomicon, so it'd be great to have some material on that!

In particular, one point of confusion seems to be about shared mutability with raw pointers (see e.g. this forum thread): People read "you need UnsafeCell whenever you mutate data behind a shared reference", and then generalize this to "whenever you mutate shared data" and think that they might need UnsafeCell even when the data they mutate is just behind a *mut T. We should point out somewhere that mutating behind a &*mut T is not in contradiction with any aliasing assumptions made by the compiler.

Illegal cpu instruction on OOM?

On the Allocating Memory page it is said that the oom langitem aborts the program by executing an "illegal cpu instruction":

The standard library calls std::alloc::oom(), which in turn calls the the oom langitem. By default this just aborts the program by executing an illegal cpu instruction.

I've checked the code and found out that the alloc lib actually calls std::sys::abort_internal in rust_oom (alloc::alloc::handle_alloc_error โ†’ std::alloc::rust_oom), which resolves for example into a libc::abort call on unix, which doesn't call any illegal instructions in my understanding but rather sends a SIGABRT to the process.

Please correct me if I'm wrong, it might be that I've got the whole idea wrong, but I believe that phrase

. By default this just aborts the program by executing an illegal cpu instruction.

should be replaced with something like

, which aborts the program in a platform-specific manner.

Thanks!

Sharp jump in difficulty of the subtyping chapter

In https://doc.rust-lang.org/nightly/nomicon/subtyping.html:

The beginning of the chapter, up to "Variance", is easy to follow. Cat extends Animal, lifetimes are bigger or smaller = easy.

But then the language suddenly changes to purely abstract academic terminology like "type constructor in Rust is any generic type with unbound arguments", and you've lost me. When you say "constructors in Rust", I think of pub fn new() -> Self and that Vec::new() has an generic argumentโ€ฆ but that doesn't explain variance to me at all.

The variance section switches to using F<Sub> and F<Super> instead of continuing the analogy of Cat and Animal. This makes it harder to understand, because you've just explained to me relationship based on cats and dogs, so now I wonder "is F<Sub> the Cat?"

Note about contravariance mentions "higher rank lifetimes" without any prior introduction to what "higher rank" is, how lifetimes have a "rank", etc.

The section later comes back to cats and dogs example, but at this point I'm completely confused and exhausted.

I think it'd help to fully explain variance based on cats and dogs before explaining variance in academic terms.

Send and Sync need some documentation

The current documentation has a TODO: better explain what can or can't be Send or Sync. Sufficient to appeal only to data races?

The explanation on what can and can't be Send or Sync would be very useful. For instance, it could contain why LinkedList is Send while it contains pointer fields that are not Send and this is Ok and the changes that could be introduced to this container so it actually stops being Send.

Thanks

"mdbook test" fails immediately: No such file or directory

I installed mdbook and then ran mdbook test as described in the README, but all that does is

2018-07-16 13:03:07 [ERROR] (mdbook::utils): Error: Unable to open the file
2018-07-16 13:03:07 [ERROR] (mdbook::utils): 	Caused By: No such file or directory (os error 2)

inconsistent, outdated Vec chapter

#73 switched to a newer version of the alloc API, but not the final stabilized version after the changes in rust-lang/rust#51241. It also didn't update any of the earlier code in the chapter (which still uses heap::reallocate, etc), so now the chapter is inconsistent and confusing to the reader when vec-final.md isn't composed of all the code they've been shown step-by-step so far.

A lot of the explanatory text also seems dated:

Because all the best tools for writing unsafe code are unstable, this project will only work on nightly (as of Rust 1.9.0). With the exception of the allocator API, much of the unstable code we'll use is expected to be stabilized in a similar form as it is today.

Unfortunately the mechanism for stating that your value is non-zero is unstable and unlikely to be stabilized soon. As such we're just going to take the hit and use std's Unique:

Provide working example for FFI

It took me a long time to get the "snappy" example, included in the FFI section, working on my machine. As it turned out, I needed several options in a build.rs file--one of which I didn't find until I stumbled across the rust-snappy repository.

Namely, I needed the following build.rs file:

fn main() {
    println!("cargo:rustc-link-search=native=/path/to/snappy/");
    println!("cargo:rustc-link-lib=static=snappy");
    println!("cargo:include=/path/to/snappy/");

    // Need to link C with C++ files
    println!("cargo:rustc-link-lib=stdc++");
}

This is never mentioned in the FFI section, and it was a bit frustrating. I've seen it mentioned several places that it's "easy to call C from Rust", and I think giving people everything they need to know to be successful in the Rustonomicon would help make that even more true.

In addition, specifically mentioning the necessity of the stcd++ library for linking to C wrappers around C++ libraries would be great. I'm sure a lot of people are already familiar with that concept, but many others (myself included) don't know about it and would need a lot of trial and error to get that to work.

Do others agree? I'm willing to attempt a write-up if I could get some feedback on where it would be appropriate to include. My thought is to put it near the beginning so that people can get a working example from the beginning, but I'm probably biased since I just went through the experience of being confused.

Elaborate mutable globals?

Hi,

I started reading the Rustonomicon. This is a great read. thx!

https://doc.rust-lang.org/nomicon/references.html

Tragically, plenty of data doesn't reside on the stack, and we must also accommodate this. Globals and thread-locals are simple enough to model as residing at the bottom of the stack (though we must be careful with mutable globals). Data on the heap poses a different problem.

I think I understand well up to this point about paths in the ownership tree, but get lost as to why mutable globals need a special attention.
Can you elaborate this part?

Thx

Providing foreign globals

The current discussion only mentions how to access foreign globals. That is, if a global variable is provided in some C library, how to access it from Rust.

How does one provide foreign globals from Rust, so that C libraries can access them (e.g. via extern ...) ?

True? "In Rust, subtyping derives entirely from lifetimes." [Subtyping page]

Although it is true that Rust does not support structural subtyping nor record subtyping, it does support nominal subtyping with regard to the relationship between structs (or traits) and the trait they implement, much like the relationship in C++ between some class and the C++ abstract base it inherits from. This check is not a lifetime check, because we are not checking on lifetime scopes, but rather whether the requested type coercion is valid based on information known about the subtype relationship between the two types.

Some evidence for this is visible in the fact that Rust will flag as a compile error whenever an attempt is make to coerce a struct reference (&S) into a trait reference (&T) that the struct does not implement. Reversing them (trait to struct) will also fail. I suspect, though I have not tested it, that this subtyping check is handled properly when dealing with contravariance (e.g., fn(T) -> U). If not, shouldn't it be for the sake of type safety?

There is yet another form of subtyping that Rust supports, besides lifetime-based and trait/struct-based: the ability to coerce a reference from &mut T to &T (but not the reverse)

I would recommend broadening this sentence to say something more like: "In Rust, subtype checks focus on lifetimes, traits, and the mutability of the reference" and then provide a few extra paragraphs that briefly describe these relationships. I think it is appropriate to focus mainly on lifetimes throughout the rest of the page, as that is the most common subtyping failure that readers will run into.

FFI section suggests dangerous practice of using empty opaque type

The FFI section of the Nomicon currently suggests to use an empty type for "opaque pointer types". That seems pretty dangerous. This came up in the discussion at rust-lang/rfcs#1861, and also in https://internals.rust-lang.org/t/recent-change-to-make-exhaustiveness-and-uninhabited-types-play-nicer-together/4602 many were not happy with using the empty type here.

Until we have proper opaque types, the safer suggestion seems to be do use a ZST with a private field, rather than an empty type. Then at least the type is not actively lying.

Change Vec Drain implementation to support ranges

Hi,
I was thinking to change Drain implementation a bit - to support Ranges like in std library - this should be nice use case for problem when leaking Drain - as original vector will not be updated at all - so nothing gets drained.

I'm beginner - so this is what I tried:

 pub struct Drain<'a, T: 'a> {
    vec: &'a mut Vec<T>,
    iter: RawValIter<T>,
    start: usize,
    end: usize,
}

and it's constructor in vector:

pub fn drain<R>(&mut self, range: R) -> Drain<T>
    where
        R: RangeBounds<usize>,
    {
        unsafe {
            let start = match range.start_bound() {
                Bound::Unbounded => 0,
                Bound::Included(s) => *s,
                Bound::Excluded(s) => *s + 1,
            };

            let end = match range.end_bound() {
                Bound::Unbounded => self.len(),
                Bound::Excluded(e) => *e,
                Bound::Included(e) => *e + 1,
            };
            assert!(start < self.len(), "invalid lower bound");
            assert!(end <= self.len(), "invalid upper bound");
            assert!(
                start <= end,
                "invalid bounds, lower must be less or equal then upper"
            );

            let iter = RawValIter::new(&self[start..end]);

            Drain {
                start: start,
                end: end,
                iter: iter,
                vec: self,
            }
        }
    }

then we can update Drop just to get rid of drained range:

impl<'a, T> Drop for Drain<'a, T> {
    fn drop(&mut self) {
        // pre-drain the iter
        for _ in &mut self.iter {}
        // now we need to fix vector - set correct len and moved drained parts our of buffer
        self.vec.remove_range(self.start, self.end)
    }
}

and vec's remove_range is:

fn remove_range(&mut self, start: usize, end: usize) {
        unsafe {
            let old_len = self.len;
            let to_remove = end - start;
            self.len = old_len - to_remove;
            // copy memory only if something was removed and something will remain in vector
            if self.len > 0 && to_remove > 0 && end < old_len {
                ptr::copy(
                    self.ptr().offset(end as isize),
                    self.ptr().offset(start as isize),
                    old_len - end,
                )
            }
        }
    }

I hope I get it right.

Can we have some more info on Send and Sync

The current documentation has a TODO: better explain what can or can't be Send or Sync. Sufficient to appeal only to data races?

The explanation on what can and can't be Send or Sync would be very useful. For instance, it could contain why LinkedList is Send while it contains pointer fields that are not Send and this is Ok and the changes that could be introduced to this container so it actually stops being Send.

Thanks

Confusing explanation of covariance for collection types (e.g. Box<T> and Vec<T>). [Subtyping and Variance section]

The Nomicon has an entire section on variance which more or less makes sense to me, except one paragraph in regards to Box<T> and Vec<T> being (co)variant over T.

Box and Vec are interesting cases because they're variant, but you can definitely store values in them! This is where Rust gets really clever: it's fine for them to be variant because you can only store values in them via a mutable reference! The mutable reference makes the whole type invariant, and therefore prevents you from smuggling a short-lived type into them.

In particular the line (emphasis mine)

it's fine for them to be variant because you can only store values in them via a mutable reference!

is vague as there is no example that shows what is meant by "storing values in via a mutable reference". It is also not clear (at least to me) what the mutable reference is taken with respect to. Is it to the collection type? Or to the elements stored within the collection type?

Furthermore, this line

The mutable reference makes the whole type invariant

is unclear as it does not explain the mechanism that allows the mutable reference to make the whole type invariant.

I'm opening this issue because I've asked this question on the Rust subreddit and Stack Overflow and got no satisfactory answers, so I take it that it is also confusing others out there.

Expand discussion of FFI safety

This is a port of rust-lang/rust#31227.

It is documented nowhere that primitives and thin pointers are FFI-safe

In particular, only thin pointers are FFI safe, and not fat pointers. This is somewhat confusing.

Also, it isn't documented (AFAICT) that one can pass a reference or mutable reference where an extern "C" function takes a const pointer or non-const pointer.

There's a lint for this, at least when declaring extern functions

I checked that and it works, i was wondering is there anything for the reverse - for e.g.: this.

The other function find does not give any warning. Though I understand that all it says is it follow C calling convention and no name-mangling none of which might warrant the parameter type warning, it is usually intended to be called form another language (with C compatibility) so should generate a warning in this case too ? Passing a callback from C for instance will be Undefined Behavior in this case. I was also stung by this:

#[no_mangle]
pub unsafe extern "C" fn(cb: extern "C" fn([u8; 32])) { // WRONG - should be fn(*const [u8; 32])
    let arr = [8u8; 32];
    cb(arr); // WRONG - should be &arr
}

Of-course looking back it was a stupid mistake - but i had subconciously assumed array would decay into a ptr like in C when callback was invoked. All my tests passed as they were written in rust. Only when interfacing with C and running into occassional seg-faults that i realised the mistake. If there was a warning or some language feature to mark that this function is not only for C ABI compatibility but also strictly for a call from C (like what extern "C" { } block does) it would be helpful.

Same with repr(C) - there is no warning for completely non-C struct having this attribute - i understand the reasoning from the docs for this but again if there is some lint/attribute to mark strict C only conformance then that would be great - as you know it's pita to debug C errors otherwise :(

Clarify FFI-safety of Option<extern "C" fn>

Relatively new to Rust, and reading through the Nomicon to make sure I build a new FFI module safely. I noticed one omission in https://github.com/rust-lang-nursery/nomicon/blob/master/src/other-reprs.md.

#13 added language to call out that Option<&T> is FFI-safe as long as *const T is FFI-safe. Based on rust-lang/rust#40913, it seems that the preferred way of expressing nullable function pointers (returned via FFI) is using Option<fn> (or I guess Option<extern "C" fn> since function pointers set by C code are presumably also C-ABI).

If my understanding is correct, shall we add this to the Nomicon? I'm happy to send a PR if that's desired.

Also, are Option<&T> and Option<extern "C" fn> an exhaustive list of repr(C) FFI-safe enums with fields, or are there other exceptions to document as well?

"Limits of Lifetimes" example now compiles

struct Foo;

impl Foo {
    fn mutate_and_share(&mut self) -> &Self { &*self }
    fn share(&self) {}
}

fn main() {
    let mut foo = Foo;
    let loan = foo.mutate_and_share();
    foo.share();
}

The above example, from https://doc.rust-lang.org/stable/nomicon/lifetime-mismatch.html, should now (as of Rust 2018), compile. I guess it is because of NLL, but I am not 100% sure.

Example ran on Rust Playground compiles with Rust 2018 but doesn't on Rust 2015 (with the error explained in the book).

repr(C) vs drop flags confusion

Hi,
the chapter on repr(C) says:

If the type would have any drop flags, they will still be added

but the chapter on drop flags says:

The drop flags are tracked on the stack and no longer stashed in types that implement drop.

Is the remark in the section on repr(C) outdated? (Or am I maybe missing something?)

Thanks!

Document when we *do* guarantee that drop runs

In my understanding, we do in some circumstances guarantee that drop runs. For example:

struct Guard;

impl Drop for Guard {
    fn drop(&mut self) {
        println!("Hello!");
    }
}

pub fn test(f: impl FnOnce()) {
    let _guard = Guard;
    f();
}

Here, we guarantee that no matter the environment or whatever f does, if the stack frame of test ever gets popped or otherwise "deallocated", then the println! certainly happens. For example, f might loop forever or abort the process, but it cannot "return" or "unwind" or finish in any other way that would circumvent the printing, nor can it use longjmp to skip test's cleanup, not can it just ask the OS to outright kill the current thread (without tearing down the entire process).

(By "guarantee" I mean "we consider it okay for safe libraries to rely on this and cause UB if it gets broken" -- but there is no immediate language-level UB caused by this, so if you do this kind of skipping of destructors in a controlled way, say for your own code which you knows has nothing droppable on the stack, then you are fine.)

This is needed to actually realize the pinning drop guarantee, but it seems not to be documented anywhere explicitly?

Cc @gankro @nikomatsakis @comex

Nomicon doesn't mention that dereferencing an unaligned pointer is UB

The Nomicon and the Reference don't say that dereferencing a misaligned pointer is undefined behavior, but I certainly would expect it to be. Checked with @gankro on IRC and he agreed.

To verify, I compiled the following:

extern {
fn f() -> *const i32;
}

fn main() {
println!("{}", unsafe { *f() + 1 });
}

This produces LLVM IR containing the following code for the call:

%3 = tail call i32* @f()
%4 = load i32, i32* %3, align 4
%5 = add i32 %4, 1

The align 4 in the load instruction indicates that LLVM should assume that the pointer is aligned on a four-byte boundary. The LLVM IR docs for load say, "Overestimating the alignment results in undefined behavior."

See also: rust-lang/reference#49

"Probably" typo

Expected (correction bolded):

As an aside, while Send and Sync are unsafe traits, they are also automatically implemented for types when such derivations are probably safe to do.

Actual (typo bolded):

As an aside, while Send and Sync are unsafe traits, they are also automatically implemented for types when such derivations are provably safe to do.

Fixed by #136

Compiler Errors Show Older Error Format

It seems that many chapters show compiler errors in the older (much less readable) compile error format.

For example the chapter on limits of lifetimes shows the following error:

<anon>:11:5: 11:8 error: cannot borrow `foo` as immutable because it is also borrowed as mutable
<anon>:11     foo.share();
              ^~~
<anon>:10:16: 10:19 note: previous borrow of `foo` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `foo` until the borrow ends
<anon>:10     let loan = foo.mutate_and_share();
                         ^~~
<anon>:12:2: 12:2 note: previous borrow ends here
<anon>:8 fn main() {
<anon>:9     let mut foo = Foo;
<anon>:10     let loan = foo.mutate_and_share();
<anon>:11     foo.share();
<anon>:12 }
          ^

when it probaly should show this:

error[E0502]: cannot borrow `foo` as immutable because it is also borrowed as mutable
  --> src/lib.rs:11:5
   |
10 |     let loan = foo.mutate_and_share();
   |                --- mutable borrow occurs here
11 |     foo.share();
   |     ^^^ immutable borrow occurs here
12 | }
   | - mutable borrow ends here

Copy error in explanation of contravariance

In the Variance section of chapter 3.8 (Subtyping and Variance), the explanation of contravariance mistakenly repeats "F<U>" instead of comparing "F<T>" and "F<U>". I have highlighted the relevant part in bold:

  • F is covariant over T if T being a subtype of U implies F<T> is a subtype of F<U> (subtyping "passes through")
  • F is contravariant over T if T being a subtype of U implies F<U> is a subtype of F<U> (subtyping is "inverted")
  • F is invariant over T otherwise (no subtyping relation can be derived)

The line should instead read:

  • F is contravariant over T if T being a subtype of U implies F<U> is a subtype of F<T> (subtyping is "inverted")

Implementing Vec final code does not compile

The final example does not compile (just clicking the run button), with following messages:

   Compiling playground v0.0.1 (file:///playground)
warning: use of deprecated item 'std::heap::Heap': type renamed to `Global`
 --> src/main.rs:9:32
  |
9 | use std::heap::{Alloc, Layout, Heap};
  |                                ^^^^
  |
  = note: #[warn(deprecated)] on by default

warning: use of deprecated item 'std::heap::Heap': type renamed to `Global`
  --> src/main.rs:34:27
   |
34 |                 let ptr = Heap.alloc(Layout::array::<T>(1).unwrap());
   |                           ^^^^

warning: use of deprecated item 'std::heap::Heap': type renamed to `Global`
  --> src/main.rs:38:27
   |
38 |                 let ptr = Heap.realloc(self.ptr.as_ptr() as *mut _,
   |                           ^^^^

warning: use of deprecated item 'std::heap::Heap': type renamed to `Global`
  --> src/main.rs:47:29
   |
47 |                 Err(err) => Heap.oom(err),
   |                             ^^^^

warning: use of deprecated item 'std::heap::Heap': type renamed to `Global`
  --> src/main.rs:61:17
   |
61 |                 Heap.dealloc(self.ptr.as_ptr() as *mut _,
   |                 ^^^^

error[E0308]: mismatched types
  --> src/main.rs:38:40
   |
38 |                 let ptr = Heap.realloc(self.ptr.as_ptr() as *mut _,
   |                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `std::ptr::NonNull`, found *-ptr
   |
   = note: expected type `std::ptr::NonNull<std::heap::Opaque>`
              found type `*mut _`

error[E0308]: mismatched types
  --> src/main.rs:40:40
   |
40 |                                        Layout::array::<T>(new_cap).unwrap());
   |                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected usize, found struct `std::heap::Layout`
   |
   = note: expected type `usize`
              found type `std::heap::Layout`

error[E0599]: no method named `oom` found for type `std::heap::Global` in the current scope
  --> src/main.rs:47:34
   |
47 |                 Err(err) => Heap.oom(err),
   |                                  ^^^

error[E0308]: mismatched types
  --> src/main.rs:61:30
   |
61 |                 Heap.dealloc(self.ptr.as_ptr() as *mut _,
   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `std::ptr::NonNull`, found *-ptr
   |
   = note: expected type `std::ptr::NonNull<std::heap::Opaque>`
              found type `*mut _`

error: aborting due to 4 previous errors

Some errors occurred: E0308, E0599.
For more information about an error, try `rustc --explain E0308`.
error: Could not compile `playground`.

To learn more, run the command again with --verbose.

Also previous examples during Implementing Vec chapter are using even older allocator API then this final code.

Wrong example in `subtyping.md`?

The subtyping section has the following text:

If mutation is by-value, then the old location that remembers extra details is moved out of, meaning it can't use the value anymore. So we simply don't need to worry about anyone remembering dangerous details. Put another way, applying subtyping when passing by-value destroys details forever. For example, this compiles and is fine:

fn get_box<'a>(str: &'a str) -> Box<&'a str> {
    // String literals are `&'static str`s, but it's fine for us to
    // "forget" this and let the caller think the string won't live that long.
    Box::new("hello")
}

If mutation is by-reference, then our container is passed as &mut Vec<T>. But &mut is invariant over its value, so &mut Vec<T> is actually invariant over T. So the fact that Vec<T> is covariant over T doesn't matter at all when mutating by-reference.

Explanations of subtyping always push me to my limit, so when I can't see how the example relates to the discussion, my first assumption is not "Oh, the text is wrong"! But I suspect that the example here is just the wrong text, because there's no mutation going on anywhere, nor any &mut Vec<T>.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.