HNHacker News
TopNewBestAskShowJobs

eddyb

465 karma · joined February 28, 2014

submissionscomments
eddyb··on Undefined vs. Unsafe in Rust
Okay I just properly took a look at this, and some of the sibling responses are only partially correct. It's not symbol names that change, but the crate does appear to have a hash used to sanity-check against this.

I used these sources:

    // a.rs
    #![crate_type = "lib"]
    pub struct Foo { pub x: i32 }

    // b.rs
    #![crate_type = "lib"]
    extern crate a;
    pub fn foo() -> a::Foo { a::Foo {} }

    // c.rs
    extern crate b;
    fn main() {
        println!("{}", b::foo().x);
    }
The field `x` from crate `a` was originally not there, and added after compiling crate `b`. The error when trying to compile crate `c` was:

    error[E0460]: found possibly newer version of crate `a` which `b` depends on
     --> c.rs:1:1
      |
    1 | extern crate b;
      | ^^^^^^^^^^^^^^^
      |
      = note: perhaps that crate needs to be recompiled?
      = note: crate `a` path #1: liba.rlib
      = note: crate `b` path #1: libb.rlib
eddyb··on Undefined vs. Unsafe in Rust
You sure you meant to reply to the follow-up to my comment? None of the hashes you mentioned is actually used by the compiler, except to detect changes wrt incremental recompilation. (EDIT: and to catch recompiled crates, see: https://news.ycombinator.com/item?id=16004250)

Distinguishing between crates is done solely through their name and -C metadata values provided by Cargo.

Once crates are loaded by the compiler based on either their explicit path (via --extern) or by being a dependency of another dependency (and there the name and -C metadata prevent collisions), "two versions of the same crate" appears no different than "two different crates".

The Rust compiler tends to "index" information (e.g. turning strings into various IDs) as quickly as possible, so a lot more semantics are "by identity" than "by syntax", and that helps when multiple identities may share a name.

That includes compiling against already compiled crates, instead of header files you have serialized semantic types and functions, which all use proper identities to "name" anything they use in turn - you never have to be looking for a definition, or risk using the wrong one.

eddyb··on Undefined vs. Unsafe in Rust
No file contents, that would defeat incremental recompilation.
eddyb··on Undefined vs. Unsafe in Rust
In that specific context, crates that have the same name within a Cargo crate graph but aren't the same exact crate - this is pretty much always about semver versions, when incompatible ones are required from different parts of the crate graph, you can end up with e.g. serde-1.0 and serde-0.9, and they get compiled with different -C metadata values, into which "1.0" and "0.9" were factored in.
eddyb··on Undefined vs. Unsafe in Rust
That changed with incremental recompilation, to allow reuse across changes.

However, Cargo will still ensure different versions of the same crate have different symbols, by passing its own hashes to rustc via -C metadata.

eddyb··on Mrustc: a Rust compiler written in C++
Destructor scopes are determined from syntactical rules and lifetimes don't play any role in them. OTOH, lifetimes are restricted by the shape of the destructor scopes.
eddyb··on Announcing Rust 1.20
It's just for an example - see https://doc.rust-lang.org/std/f32/.
eddyb··on How JavaScript works: inside the V8 engine
No worries, I'm just surprised you managed to find a simple testcase that exhibits it, so, really, thank you!

I've opened a (a bit vague for now) issue so at least I don't forget about it: https://github.com/rust-lang/rust/issues/44041

As for phase ordering - someone did show me recently VSDG, which claims to solve at least part of the problem (specifically, https://www.cl.cam.ac.uk/techreports/UCAM-CL-TR-705.pdf and http://sro.sussex.ac.uk/7576/1/Stanier%2C_James.pdf).

eddyb··on How JavaScript works: inside the V8 engine
This looks like yet-another-phase-ordering-issue in LLVM. I hit so many of these it's not even funny. Arguably one could find C code that shows the exact problem under clang. Example of optimized LLVM IR for that sample (press LLVM IR on https://play.rust-lang.org/?gist=b60ec85886eff967b21c7abc899...):

    br i1 false, label %bb9, label %bb6
SimplifyCFG would instantly kill that. But it doesn't get to run again after whatever simplified the branch condition. I've heard rumors that LLVM is working on a new pass manager, maybe they will eventually fix this pervasive issue.
eddyb··on Rust RFC 2094: non-lexical lifetimes
> There's no way to give a return value the lifetime of the caller, not the callee.

That is, in the general case, not physically possible, and this sort of thing only works in GC languages because they return GC pointers to dynamically allocated data. It certainly doesn't exist in C or C++ (unless I completely misunderstood what semantics you wanted).

AFAIK there is no implementation of anything (not even JITs) which can allocate on the caller's stack. The closest anything gets is Forth where the call stack and data stack are separate and returning just leaves data on the stack for caller to read.

Without a split stack, returning something larger than a register (or two) is done by passing a pointer to space on the caller's stack, to the callee.

There have been very specific schemes proposed for `-> [T]` in Rust, e.g. the slice is left of the callee's stack, and a pointer to it returned, so the caller can allocate that much stack space and memmove'd it up.

But that wouldn't help with "allocating" on the caller's caller's stack because moving the data would invalidate references to it (and if you can have a reference to something, you can use it in ways the compiler can't trace it, unlike a GC).

eddyb··on Rust RFC 2094: non-lexical lifetimes
> This may be more of a "because we can" feature.

What I see is actually "it's wanted/needed enough that it's worth putting in the huge effort to make such a complex (and novel?) system reality".

EDIT: to be more clear: what's new here isn't "exposing liveness at the source level" but "model liveness in region typing". If we were changing when the destructor runs (which we can't because backwards compatibility - also, doubtful we'd want that at all), that would be more "source level".

eddyb··on Rust: Not So Great For Codec Implementing
FWIW `cargo vendor` already exists, just not part of Cargo itself, but rather a tool by one of the core devs.

It's even used for releasing the official Rust tarballs as we now employ crates.io dependencies in the standard library and the compiler.

eddyb··on Zillow forces McMansion Hell to delete posts
http://www.mcmansionhell.com/ is still up, for now, at least not all the posts are gone, so have a look.
eddyb··on Bugs You'll Probably Only Have in Rust
:(
eddyb··on Glob Matching Can Be Simple and Fast Too
Why just deprecate? Can't it be made to proxy behind the scenes to globset?
eddyb··on Optimizing Rust Struct Size
You can get it from debuginfo - just load your binary into any debug session (this might even work with no execution or core dump, just a blob of memory that from your program) and then you can cast char* to YourRustType* to be able to introspect it.

On top of that, on nightly you have this flag: -Z print-type-sizes -- print layout information for each type encountered

This is what the output looks like: https://github.com/rust-lang/rust/blob/13fd5e93deb41045c4de8...

eddyb··on Optimizing Rust Struct Size
> It's just that it wasn't enforced before.

To be clear, this "enforcing" is now the fact that your fields, if you forgot #[repr(C)], might be in a different order than what you assume.

But it has been linted for a very long time, at least in the direct interactions with FFI, you'd have to be casting pointers to/from C to hide that you're using a Rust struct for C data, from that lint.

eddyb··on Optimizing Rust Struct Size
Not by default, no, but what I mean that if you're debugging you can just enable debuginfo and that will contain the descriptions of the types including all fields, with the right offsets and everything.
eddyb··on Optimizing Rust Struct Size
The correct type layout is already encoded in debuginfo, so I'm not sure how that's a problem unless you're avoiding a debugger intentionally or simply can't use one?
eddyb··on Optimizing Rust Struct Size
> now needs 'repr(C)'

It always has, we lint for this, and the Rust struct layout has been officially left unspecified (making assumptions based on it UB) from before 1.0, anyway.

> One would think that would be 'repr("C")', since here, "C" is neither a reserved word nor a variable, but, whatever.

Neither is 'repr' - they're both just identifiers in an attribute. #[attr("string literal")] is newer and still unstable (as part of the new macro system).

The bit array packing is something we've wanted to do for a long while, but it requires an opt-in along the lines of "disallow taking references to any (sub-byte) fields".

#[repr(packed)] is the same as the C equivalent, be it attribute or pragma, in that it only removes alignment padding and does nothing to booleans.

eddyb··on Functional Language Features in Rust – Iterators and Closures
Indeed, the way you would be generic over generic closures would be "type HRTB" (e.g. F: for<T: Clone> Fn(&T) -> T), which depends on the trait system overhaul, just like ATC.
eddyb··on Rust's language ergonomics initiative
This idea has been waved around a bit, but in the form of `impl Error` where `_` is. That is, inside the function the `E1 | E2 | ...` type is being built, and if you have automatic dispatch for `Error`'s methods then it will work with `impl Error`.

Actual global inference has never been on the table and still isn't.

eddyb··on Next Iteration of “The Rust Programming Language” Book
No lifetime parameters can be shorter than the lifetime of the struct itself, and that's part of the so-called WF (well-formed(ness)) rules in Rust (which the compiler tries to make as implicit as possible).

However, that has nothing to do with why you have to have a parameter. The parameter is there to make all instances of App track the actual lifetime, e.g. you can tell between App<'foo> and App<'static> (and in the former case, it can keep the borrow that made the &'foo Config alive for as long as the App<'foo> sticks around, and if this is longer than the underlying data lives for, you get an "use after free" error).

Combined with function signatures, Rust can track complex interactions without ever looking at callee bodies, and if you're never reusing a lifetime parameter, you get just as much flexibility as if you were passing the references around directly.

This is something that the C++ attempts at tracking scopes and enforcing a set of rules about them, don't seem to have figure out yet. You need a certain amount of annotations to keep the correct mapping, otherwise you just lose information and have to assume every struct that contains a reference may borrow everything that was borrowed at some point and the borrow may have "escaped" into a struct.

You're either too conservative and thus can't apply the rules to prevent iterator invalidation and data races like Rust can with borrows (Cyclone didn't have borrow-checking either IIRC), or you're ignoring an entire subset of UAF bugs waiting to happen.

Even for the purpose of preventing UAF, such an imprecise aliasing-analysis-like conservative system may be too restricting for many real-world usecases, whereas Rust's, ironically, wouldn't be.

eddyb··on A Prettier JavaScript Formatter
Think what happens when you break a line comment (or a string, in languages where it can't span several lines). And that's an easy example at that.
eddyb··on RustgreSQL
If that is what I think it is, see https://docs.rs/indexing for something similar, crafted from invariance over higher-ranked lifetimes, instead of types.

I don't think Rust can ever replicate the Haskell implementation identically (if we had HRTB over types), as type parametrism is gone (see: specialization RFC). However, lifetime parametrism serves a similar role in Rust, and lifetimes are closer to a concept of "instance" than types.

eddyb··on Transitioning Firefox's rendering engine from Gecko to Servo
AFAIK that's not actually true, one option they have is going through Mesa's llvmpipe in such cases.

EDIT: to be clear, with the Servo nightlies you need to do this yourself but Firefox could bundle it eventually.

eddyb··on Channels in Golang
The only document I can think of outside of Niko Matsakis' blog (http://smallcultfollowing.com/babysteps/) would be https://github.com/rust-lang/rust/blob/master/src/librustc/i...
eddyb··on Zig: a system language which prioritizes optimality, safety, and readability
Are you referring to unstable APIs being deprecated and removed?

Technically we can remove those at any time (happens in librustc all the time), the deprecation period is to help and motivate migration of nightly users, nobody else can touch those APIs.

eddyb··on Writing a JPEG Decoder in Rust – Part 2: Implementation I
The example can be written like this though:

  let codes: Vec<HuffmanCode> = data_table.iter()
            .zip(&code_lengths)
            .zip(&code_table)
            .map(|((&value, &length), &code)| {
                HuffmanCode {
                    length: length,
                    code: code,
                    value: value,
                }
            })
            .collect();
eddyb··on Zero-cost futures in Rust
Do note that with MIR the focus is polymorphic optimizations - reducing the LLVM IR for all monomorphizations of a generic function, at once.

Unlike C++ templates, Rust enforces a single definition with uniform semantics, for a generic type/function (specialization going through the existing trait static dispatch mechanism), so we can take advantage of that to reduce compile times.

← PreviousPage 3 of 6Next →