notes-computer-programming-programmingLanguageDesign-prosAndCons-rust

" Go and Rust are not really competing. There may be some overlap in domain, but they occupy different niches and will likely appeal to different crowds. Go will appeal more to people who prefer its focus on conceptual simplicity and its "opinionated" nature (very much like Python). Rust will appeal more to people who prefer "functional" trimmings (algebraic datatypes, pattern matching, liberal use of higher-order functions, Scheme-style macros) as well as folks fed up with C++ who aren't willing to give up complete control over memory and performance characteristics. The fact that both languages are built around a similar concurrency model is just convergent evolution.

'

"

shanemhansen 117 days ago

link

Go actually provides what I believe to be zero cost abstractions that map straight to the c memory model, making it very easy to use for systems programming.

If you want a fixed size array of bytes, for for it. Zero overhead. If you want a slightly smarter list you can use a go slice, which is also pretty low overhead. I've personally verified that if I create a struct in go it takes up the exact same amount of memory as the same c structure.

As a concrete example, I recently built an interface to kernel crypto apis using pure go (no cgo). There's no magic to go data structures, you can define structs in go and pass pointers directly to syscalls (through the syscall package) and everything just works. Dealing with large arrays of binary data is similarly straightforward. So go does give you complete control over memory layout if you choose to use it.

The elephant in the room is that go does have garbage collection, and that ain't free. Practically you can minimize it's impact by managing your own memory. In fact that's what Brad Fitzpatrick is doing with his go based memcached implementation.

It all boils down to how you define systems programming. I guess if you mean is go suitable to write a kernel in, the answer is probably no, (but it would sure be fun to try). If systems programming requires having the complete ability to interact with the operating system and all syscalls + tight control over memory layout, then maybe go is the way to go.

https://github.com/shanemhansen/gocryptodev/

[edit]

with respect to python and garbage collection, did you know you can actually turn it off? If you have no cycles in your program you can turn off gc and let the ref counter clean up all your data, similar to Objective-C's ARC.

http://docs.python.org/library/gc.html


pcwalton 117 days ago

link

There is a lot more to zero-cost abstraction than memory layout. If you do manual memory management in Go, then you get no safety. It is also difficult to use the standard library with manual memory management. Furthermore, interfaces in Go are not zero-cost; you incur virtual dispatch whenever you use them.

The article is about doing safe manual memory management. That's something that's unique to Rust. "

"

Rust is not particularly object-oriented; idiomatic Rust code leans more toward the functional style as opposed to the OO style.

The language is designed to make it harder to write buggy code at the cost of some amount of language complexity. Whether this is an acceptable tradeoff will depend on your particular situation. "

---

" On the Rust side, algebraic data types, an AST macro system, saner tuples, non-nullable and immutability by default. I'm a bit skeptic about the different pointer types and how they will interact with meta-programming.

On the D side, many good things like unittest(), best-in-class overloading support, compile-time everything, etc.

http://laser.inf.ethz.ch/2012/slides/Alexandrescu/2-D%20course%20parts%201%20and%202.pdf

"

---

"

cmccabe 117 days ago

link

Rust has three different ways of allocating memory. And you can't pass things allocated one way to functions that expect something allocated the other way.

A Python replacement, this ain't. More like C++ on quaaludes.


pcwalton 117 days ago

link

No, Rust has two ways of allocating memory (on the task heap or on the exchange heap), and most functions take borrowed pointers, which accept both kinds of memory.

(Edit, re below reply: It depends how you define "allocation"; it's either two or three. I wasn't considering the stack as allocation, but you're right that Rust takes the traditional stack/heap distinction and expands it to stack/exchange heap/task heap. We should update the tutorial to make it clear that borrowed pointers work for the stack as well as both heaps.)

I agree that the language is not designed to be a Python replacement, however. No language is suitable for every task.


cmccabe 117 days ago

link

from http://dl.rust-lang.org/doc/tutorial.html

"7.3 What to be aware of

Rust has three "realms" in which objects can be allocated: the stack, the local heap, and the exchange heap. These realms have corresponding pointer types: the borrowed pointer (&T), the shared box (@T), and the unique box (~T). These three sigils will appear repeatedly as we explore the language. Learning the appropriate role of each is key to using Rust effectively."

It's 3 different types, not 2.


eevee 116 days ago

link

Rust statically prevents leaks and double-frees, warns about implicit copies of large structures, and has GC built in if I want to use it. It's certainly more familiar to a Python dev than C++. The whole trait system is even like a built-in and vastly simpler zope.interface. "

---

"

kibwen 117 days ago

link

I'm sure that Go has higher-order functions, I just mean that Rust encourages you to use higher-order functions all the time. For example: in Rust, `for` loops desugar to higher-order functions (it's very neat, actually). Furthermore, whereas in Go you spawn a goroutine with the `go` keyword, spawning a task in Rust is done by handing a function (usually a Ruby-esque closure) to the `task::spawn` library function. (Note also that "higher-order functions" are not the same as "first-class functions".)

Go does not have pattern matching, to my knowledge, though without algebraic datatypes I don't think it's really a big deal. "

---

" He never specifically said it's C++ nor Java. I think he's talking about C++. C++'s slow compile problem has to do with the repeated inclusion of header files for every source file, which can become a FileNum?^2 recompilation problem. Though pre-compiled header files and refactoring into pre-built libraries should reduce the compile time. Java simply does not have the slow compile problem due to its simple package/class per dir/file, where a compiled class won't be recompiled again. "

--

a gazillion times in the discussion https://news.ycombinator.com/item?id=6417319 , various people (or maybe one person over and over, i didn't check) claim that although Go isn't a good C++ replacement, Rust might be

--

upvote

derefr 23 hours ago

link

Sounds like D would be better-compared to Rust, then.

reply

upvote

pcwalton 23 hours ago

link

Rust has safe manual memory management, at the cost of a learning curve. In D you must use the garbage collector if you want memory safety, but it avoids all the complexity of lifetimes and uniqueness. This difference makes the two languages feel pretty different, even if at their core they're pretty similar.

reply

upvote

luikore 12 hours ago

link

D is more like C++ without macro and with GC plus some engineering features (unit test is a language feature? weird). Rust looks different and does different. For example, idiomatic foreach block in Rust is a syntax sugar of passing lambda, which is more friendly to parallelism. While in C++/D it is a syntax sugar of classic for-loop with iterator, which is more efficient in unparalleled environment.

reply

upvote

WalterBright? 11 hours ago

link

You might thing that unit test as a language feature is weird. But our experience with it is that minor-seeming weird feature has been an enormous success.

It has literally transformed writing D code, and for the better. There's another little feature, -cov, which will tell you which lines of code are covered by the unit tests.

It's hard to understate the improvement these engender.

reply

upvote

dbaupp 7 hours ago

link

> For example, idiomatic foreach block in Rust is a syntax sugar of passing lambda, which is more friendly to parallelism

This changed with the recent 0.8, `for` is now syntax sugar for using the Iterator trait, which optimises exactly like C++ (the vector iterator even vectorises when LLVM can do it).

Rust has actually moved to have iterators that are very similar to (a subset of) D's ranges.

reply

---

kibwen 5 hours ago

link

A selection of some of my favorite aspects of this release:

1. The (yet-ongoing) removal of managed pointers, leaving us with one fewer pointer type. Final tally of built-in pointer types: unique pointers, mutable references, and immutable references.

2. The dead code detection pass (https://github.com/mozilla/rust/pull/10477), contributed by a student of the University of Virginia's Rust-based systems programming class (http://rust-class.org/pages/using-rust-for-an-undergraduate-...).

3. The `Any` trait, giving us on-demand dynamic typing (https://github.com/mozilla/rust/pull/9967).

4. The clean abstraction of green threads and native threads out into their own libraries (https://mail.mozilla.org/pipermail/rust-dev/2013-December/00...) such that any library that makes use of the stdlib will work regardless of which strategy the user selects.

We're not quite in the home stretch yet, but there are very few hard blockers left on 1.0. Here's the list that I can think of:

1. Dynamically-sized types (http://smallcultfollowing.com/babysteps/blog/2014/01/05/dst-...)

2. The extension of rvalue lifetimes (http://smallcultfollowing.com/babysteps/blog/2014/01/09/rval...)

3. Struct single (note that's single) inheritance (http://smallcultfollowing.com/babysteps/blog/2013/10/24/sing...)

4. Niceties and sugar to support custom smart pointer types to complete the excision of managed pointers

As far as I know, the devs are still aiming for a 1.0 release in 2014. The 1.0 release will not necessarily mean that the language is done evolving or ready for production use, but it will mean that the developers will begin honoring language-level and library-level backwards compatibility. I would expect at least two more unstable point releases (i.e. 0.10 and 0.11) before a 1.0 release occurs.

--

vorg 18 minutes ago

link

> normalized to Unicode normalization form NFKC

I'm wondering why they chose NFKC (compatibility composed from) instead of NFC (canonically composed form).

`ª` would become `a`, losing its super type. `ᵤ` becomes `u`, losing its sub type? `Ⓐ` becomes `A`, losing its circle type. As for multi-codepoint mappings, `¼` would become three tokens `1⁄4`, where `⁄` (U+2044) doesn't map to `/` (U+002F).

--

http://www.rustforrubyists.com/

--

pcwalton 2 hours ago

link

I'd say concurrency support is a big one: Rust rules out data races at compile time, eliminating the need for race detectors, while ensuring that you still have the full array of options when it comes to shared memory, locks, and so on.

reply

--

steveklabnik 4 hours ago

link

I am primarily a Rubyist, but I'm head over heels for Rust. I'm pretty sure I've inculcated wycats with similar feels.

You might be interested in http://words.steveklabnik.com/rust-is-surprisingly-expressiv... , which is sort of about this.

reply

bascule 2 hours ago

link

Ditto for me, but I came from a largely C background prior to Ruby

reply

--

bjz_ 5 hours ago

link

As always, drop by irc.mozilla.org #rust if you'd like to chat or ask questions. We're a friendly bunch!

http://chat.mibbit.com/?server=irc.mozilla.org&channel=%23ru...

reply

Dewie 4 hours ago

link

OT: Is Rust using operator overloading? Is user-defined operators a possibility in the future? I googled it and the most I could find was this:

http://www.reddit.com/r/rust/comments/1le6vu/i_wrote_a_proto...

reply

kibwen 4 hours ago

link

Operator overloading, yes. But we place some restrictions on which types you can overload in order to keep things sane (oversimplified explanation: you must have declared the types yourself in order to overload operators on them). We also discourage the wanton overloading of operators to mean completely random things, as you might have encountered in C++.

As for user-defined operators, if you mean something like Haskell's custom infix operators then no, I don't believe there are any plans for those.

reply

JoshTriplett? 4 hours ago

link

> As for user-defined operators, if you mean something like Haskell's custom infix operators then no, I don't believe there are any plans for those.

Do the Rust designers actively oppose having them, or just not have any concrete plans to add them?

reply

kibwen 3 hours ago

link

Merely lukewarm, I think. There are simply much higher priorities to focus on at the moment. Who knows what Rust 2.0 might hold!

reply

Dewie 4 hours ago

link

I think that Reddit thread was interesting. The syntax is taken from Haskell, but someone brought up ditching precedence and only associating in one direction, which I guess is simpler than operator overloading(?)

reply

steveklabnik 4 hours ago

link

You want to check out std::ops http://static.rust-lang.org/doc/master/std/ops/index.html

reply

---

http://words.steveklabnik.com/a-30-minute-introduction-to-rust

---

As someone who has written a fair bit of Go code, I also find myself keeping a close eye on Rust. The things that really appeal to me are the possibility of immutable (by default) types, thread-local memory (ie. owned pointers), no null pointers, and generics. These are all basically aspects of the type system that make concurrent programming safer and more predictable.

--

http://jaredly.github.io/2014/03/22/rust-vs-go/index.html

--

on apple swift vs rust:

We're not scared in the slightest. I'll reconsider when Swift has inline ASM, allocators, linear types, move semantics by default, region analysis, and a type system that guarantees freedom from data races (oh, and when the code is open-sourced, and targets both Linux and Windows as a first-class citizen).

Swift isn't intended to be a systems language: it's an application language. Anyone who's capable of choosing Swift as of today was just as capable of choosing Objective-C yesterday. And as the Rust developers themselves have discovered over the past two years, deciding to become a systems language doesn't happen overnight.

--

pornel 16 hours ago

link

I hope ideas will flow the other way too, and Rust adopts some sugar from Swift.

I find `if let concrete = optional` sooo much nicer than `match optional { (concrete) => , _ => {} }`.

Rust has solid semantics that covers more than Swift. OTOH Apple has put their UX magic into the language's syntax. Some syntax shortcuts, like `.ShortEnumWhenInContext?` are delightful.

The two combined will be the perfect language ;)

reply

andolanra 10 hours ago

link

You could always write this yourself with a macro:

    macro_rules! if_let {
      ($p:pat = $init:expr in $e:expr) => {
        { match $init { $p => $e,_  => {}, } }
      }
    }
    fn main() {
      let tup = (2, 3);
      if_let!{(2, x) = tup in println!("x={}", x)}; // prints x=3
      if_let!{(5, x) = tup in println!("x={}", x)}; // doesn't print
    }

It's slightly more heavyweight, but still not too bad.

reply

--

[–]glaebhoerl 1 point 4 hours ago

If you don't care about low-level control (or even if you do), you might care about other things.

For example, my criteria for "doesn't suck" are something like:

    Statically typed
    Type-safe
    Type inference
    ADTs
    Closures
    Generics
    Type classes, or something similar
    No implicit nulls

Suddenly the arena is not so crowded.

    The sacrifices necessary to wrest low-level control have severe costs in other areas

Do you have anything in mind here besides explicit reference types and clone()?

    permalink
    parent

[–]kibwen 1 point 4 hours ago

If those are your criteria, then Haskell and Ocaml have already beaten us to maturity. Nobody who is currently using either of those languages ought to have any interest in Rust.

    Do you have anything in mind here besides explicit reference types and clone()?

Explicit lifetime annotations, closures that are nowhere near as general as in a dynamic language, explicit macro invocations, no canonical "dictionary" type, and our bountiful cornucopia of attributes. Not to mention the syntax concessions made to appeal to C++ users, which are all but a requirement to compete in this space.

    permalink
    parent

--

[–]jfagercontributor 25 points 22 hours ago*

At a glance:

Similar:

    Swift's protocols look somewhat like Rust's traits, which both look like Haskell's typeclasses.
    Both use Option instead of null.
    A lot of sameish syntax choices (type annotations come after variable names, braces, no parens around conditionals).
    Statically typed with local type inference.
    Bounds-checked arithmetic and array access.
    No/few automatic coercions.
    Forced initialization.
    ADT's via enums.
    Pattern matching.
    Generics.

Different:

    Swift doesn't have a concurrency story (or at least hasn't told it yet), Rust does (tasks, no data races, channels).
    Swift looks like it just uses stack allocation and Arc for memory management; Rust gives you much more control.
    Swift semicolons are optional.
    Swift uses separate keywords for defining value and reference types (struct vs class).
    Rust has macros; the Swift book doesn't mention any metaprogramming features.
    Rust is open source and already works on a bunch of platforms, Swift looks like its going to be proprietary and only work on Mac and iOS.
    Swift will automatically get massive adoption, Rust will have to compete on its merits.
    There's some pretty impressive tooling available for Swift out-of-the-box.
    permalink
    parent

[–]eddybsupercontributor 4 points 14 hours ago

Can we please use ARC when talking about "automatic reference counting" and Arc only for our "atomic reference-counted smart pointer"? Preferably, we should spell out the one that is less relevant to Rust.

    permalink
    parent

--

pornel 20 hours ago

link

Rust has `unsafe` blocks in which you're allowed to do all nasty hacks you want.

Rust actually isn't that complicated. Don't get discouraged by comparisons to Haskell — it's still a C-family language where you can play with pointers and mutable state.

To me Rust still feels like a "small" language (similar size as Go or ObjC?, not even scratching complexity of C++). It's mostly just functions + structs + enums, but they're more flexible and can be combined to be more powerful than in C.

reply

thinkpad20 19 hours ago

link

I think comparisons to Haskell are not too far off the mark. Haskell is not that complicated of a language either. You can do all sorts of complicated things with it, but the language itself is relatively simple. It just has a lot of stuff that you're likely to have never seen before (extensive use of higher-order functions, higher-kinded types, etc), and its type system produces error messages that seem obscure from the outside. Similarly Rust can have some rather obscure error messages that you're probably not going to have seen before during compilation - lifetime specifiers, errors about using things in the wrong contexts, heck, even "Bare str is not a type (what?)"

I'm much more familiar with Haskell than Rust, but having played around with Rust I think they're on a par with each other in terms of difficulty, depending on your background.

reply

--

dbaupp 15 hours ago

link

> You can easily generate a segfault in Rust in 'unsafe' (or 'trusted') code; that might only restrict errors of that nature to code that uses unsafe blocks, but practically speaking that's pretty common; once you enter an FFI unsafe block, you lose all type safety; but you can totally do it without FFI too. Eg. using transmute().

Not directly addressing what you're saying, but, IME people are far too quick to use `unsafe` code. One needs to be quite careful about it as there's a pile of invariants that need to be upheld: http://doc.rust-lang.org/master/rust.html#behavior-considere...

> once you enter an FFI unsafe block, you lose all type safety

You don't lose all type safety, especially not if the FFI bindings you're using are written idiomatically (using the mut/const raw pointers correctly; wrapper structs for each C type, rather than just using *c_void, etc).

reply

http://doc.rust-lang.org/master/rust.html#behavior-considered-unsafe

--

http://doc.rust-lang.org/master/intro.html

https://news.ycombinator.com/item?id=7968556

--

https://news.ycombinator.com/item?id=8011310

	An Experimental New Type Inference Scheme for Rust

---

http://smallcultfollowing.com/babysteps/blog/2014/05/13/focusing-on-ownership/

---

Bartosz Milewski Says:

September 20, 2013 at 1:30 pm

I don’t know much about Rust, but I noticed one thing: Like Erlang, it supports only one model of concurrency: message passing. That’s very limiting. Haskell actually lets you share memory in a very controlled way, which makes the implementation of STM possible.

---

[–]DrBartosz? 3 points 10 months ago

I'm concerned that Rust, like Erlang, only supports message passing style of concurrency. Message passing is relatively easy to make safe, but is not general enough and often leads to spaghetti code.

    permalink
    parent

[–]Tekmo 3 points 10 months ago

So do you prefer STM or did you have something else in mind?

    permalink
    parent

[–]DrBartosz? 2 points 10 months ago

I was thinking more of data-driven parallelism where you split a large array (or graph) between multiple workers. But there are, in general, many paradigms for concurrency and parallelism, and a general purpose language should support them.

    permalink
    parent

[–]sstewartgallus 3 points 10 months ago*

I'm actively trying to work on the non-message passing style of concurrency actually (in extra::sync, and extra::arc.) It's still quite ugly but I feel that Rust's type system will allow me to make it really pretty. Here's a sample that I hope to get working in the future (obviously a shorthand will be offered over the full RAII style using closures but the full RAII style allows really powerful techniques.)

 {
     let read_lock = rwarc.read_lock();  // The lock is acquired
     let x = read_lock.get();            // A readable pointer to the value is borrowed
     println(fmt!("%s", *x));
     // Here the lock is automatically released
 }

If anyone wants to help though that'd be awesome although mostly what's blocking progress is the long build times. Hopefully the rustpkg work will help to solve that. (Obviously, there will be convenience methods for directly getting values but the read_lock stuff will allow upgrading of the read lock to a write lock, or vice-versa.)

So, no Rust will not prohibit shared state concurrency (it just hopes to make it safer.)

Also there is no reason you won't be able to pass unsafe pointers around, and write your own concurrency library.

    permalink
    parent

---

scythe 302 days ago

link

Well, there is also the difficulty that there has not been, save Ada, a serious and concerted effort to replace C and C++ for so many of the things that they are used for. Except for Ada, no other modern language is suitable for writing things like kernels, drivers, emulators, etc; except for Ada, basically all modern languages are dependent on garbage collection.

So if you want to complain about C, remember that the only serious alternative is and has been Ada, and ask yourself why you're not using Ada, why operating systems and window managers, mupen64plus and Wayland, et cetera don't use Ada, and the truth is: people like C more than Ada.

Ada is the anti-C++. It's the answer to every criticism. It's also basically unheard of outside of situations where people really, really need safety cf. airline industry. In fact most of the people who complain about C++ would never even consider Ada. The author of the article seems to be unaware it even exists, which says a lot.

The takeaway is that safety is overrated, consistency is overrated, reliability is overrated, because if we really cared about these things... we'd use Ada. And we don't, because "Ada is annoying!".

---

" Rust let i = box 1234i;

C++ int *i = new int;

Rust infers the correct type, allocates the correct amount of memory and sets it to the value you asked for. This means that it's impossible to allocate uninitialized memory: Rust does not have the concept of null. " -- http://doc.rust-lang.org/master/intro.html

---

" First, let's go over a simple concurrency example. Rust makes it easy to create "tasks", otherwise known as "threads". Typically, tasks do not share memory but instead communicate amongst each other with 'channels', like this:

fn main() { let numbers = vec![1i, 2i, 3i];

    let (tx, rx)  = channel();
    tx.send(numbers);
    spawn(proc() {
        let numbers = rx.recv();
        println!("{}", numbers[0]);
    })}

In this example, we create a boxed array of numbers. We then make a 'channel', Rust's primary means of passing messages between tasks. The channel function returns two different ends of the channel: a Sender and Receiver (commonly abbreviated tx and rx). The spawn function spins up a new task, given a heap allocated closure to run. As you can see in the code, we call tx.send() from the original task, passing in our boxed array, and we call rx.recv() (short for 'receive') inside of the new task: values given to the Sender via the send method come out the other end via the recv method on the Receiver.

Now here's the exciting part: because numbers is an owned type, when it is sent across the channel, it is actually moved, transferring ownership of numbers between tasks. This ownership transfer is very fast - in this case simply copying a pointer - while also ensuring that the original owning task cannot create data races by continuing to read or write to numbers in parallel with the new owner.

To prove that Rust performs the ownership transfer, try to modify the previous example to continue using the variable numbers "

---

steveklabnik 3 days ago

link

Rust does not have any garbage collection, to be clear. All your other features are correct though :)

(We have previously said "opt-in GC" but that was a lie. See https://news.ycombinator.com/item?id=8312327 for more.)

reply

JoshTriplett? 3 days ago

link

Yeah, I should have said that more clearly. Automatic memory management (never having to call free), not garbage collection. Which is arguably more awesome: whenever the compiler can figure out at compile time when you'll stop using memory, it can statically decide to reclaim it there.

reply

wycats 3 days ago

link

Yes! The fact that Rust has zero-runtime-overhead automatic memory management is a big deal. I've been using Rust for almost a year, and it's been a source of great delight for me.

reply

pacala 3 days ago

link

The ownership idioms are very similar to idiomatic C++11 and std::unique_ptr. Which is to say that Rush has got an industrial strength safe memory management system.

But Rust stands out because the rest of the language is such a joy to use, compared to pretty much any other 'systems' language out there.

Congratulations to the team!

reply

wycats 3 days ago

link

One of the cool things about the Rust ownership system is the lease/borrow system. Moving is cool, but much of the time you want to synchronously call another piece of code and give it a temporary (stack-frame-long) lease for that pointer.

Rust starts with ownership, but makes it easy to ergonomically and safely lend out that ownership (including one-at-a-time mutable leases) of both stack and heap allocated pointers.

I've been programming with Rust since last December, and I have had essentially zero segfaults coming from Rust code during that time frame, roughly equivalent to what I would have expected writing code in a language whose safety guarantees come with a runtime cost (GC or ARC).

reply


MichaelGG? 3 days ago

link

Rust looks fantastic, and has a lot of things I wish I could do while in a higher level language like F#.

I just wish Rust was a bit less verbose. Requiring, for instance, type annotations on function arguments because it's sometimes helpful is such a weird decision. Let the programmer decide when an annotation is needed. This gets annoying when you get into functions with complex arguments. Especially for local functions where the signature could be messy, but the limited scope means annotations just clutter things. I'm not sure why Rust forces us to compromise here.

reply

pcwalton 3 days ago

link

> Requiring, for instance, type annotations on function arguments because it's sometimes helpful is such a weird decision.

In separate compilation you have to annotate functions anyway, and in most large ML projects I've worked on people tend to annotate just because the error messages get much better. This is a common design in functional languages these days.

reply

pestaa 3 days ago

link

Fully typed function signatures form a kind of contract you can program against.

Haskell and other extremely strongly typed languages can infer the types of function parameters, yet the community still agrees it is good practice to annotate your work.

reply

burntsushi 3 days ago

link

Interesting point. Getting the type annotation inferred for you there is definitely useful. I've used it a few times myself.

The `undefined` trick is also immensely useful. I use it a lot when starting a new module. Rust also has a notion of bottom, indicated by `!`, which will unify with all types. I frequently use this in Rust in a similar way that I use `undefined` in Haskell. (In Rust, you would speak `fail!()` or `unreachable!()` or `unimplemented!()` or define-your-own.)

> (if you've ever written a little wrapper around a function from a complicated library, you know what I mean)

Yes, absolutely. I haven't really run into this problem with Rust yet though. Types are generally pretty simple. If and when Rust gets higher-kinded types, that would assuredly change.

reply

fanf2 2 days ago

link

I thought ! in Rust indicates a macro.

reply

burntsushi 2 days ago

link

A `!` followed by an identifier does, yes. But a `!` also lets you define diverging functions:

    fn diverging() -> ! {
        unreachable!()
    }

The two `!` in that code are completely orthogonal things. See the manual on diverging functions: http://doc.rust-lang.org/rust.html#diverging-functions

reply

mercurial 2 days ago

link

OCaml has a decent compromise between no signature at all and signatures everywhere: you put signatures in your interface. Works pretty well in practice, I find.

reply

steveklabnik 3 days ago

link

Even in languages with whole-program inference, it's generally regarded as best practice to write out the types of your functions, hence Rust's choice here. You're right that there's a tradeoff. In general, Rust follows 'explicit over implicit.'

> This gets annoying when you get into functions with complex arguments.

Have you seen the where clauses yet? This should significantly help complex function declarations.

reply

MichaelGG? 3 days ago

link

I just don't understand why there has to be a tradeoff. I just don't get why the compiler should decide on such a large thing, instead of letting the programmer do it. One can always be more explicit if one feels they're getting value from it. If someone wants to write a bunch of terse code, why stop them? Does the compiler gain a large benefit from not having to include this feature? Who loses by allowing users to do what they want?

Comparing C# and F#, the extra annotations change the frequency in which I'll introduce an inner (local) function. For instance, here's a little helper in a piece of code I'm writing at the moment. It's part of a larger function and isn't exposed beyond 5 lines of code.

  let runWithLog f name =
    try run f name with ex -> logExn ex

Used in: runWithLog "sessions" collectSessionStats runWithLog "visitors" collectVisitorStats

Having to add "(f: string -> RunParams? -> Whatever -> unit) (name: string)" almost doubles runWithLog helper yet provides no benefit. And this an extremely simple case! Once the arguments are generic, higher-order functions themselves, it gets quite noisy.

Sure, if it's a top-level export, then maybe annotating is a good idea. But if it's limited in scope then what's the harm?

Not that it'll change when I use Rust - there's nothing competing in this category. It'd just be nice if the language let the user decide on the style.

reply

burntsushi 3 days ago

link

Rust will do type inference on lambdas for you. :-)

    fn fubar(x: uint) {
        let times = |n| x * n;
        println!("{} * 5 = {}", x, times(5));
    }

Rust only enforces type annotations on top-level functions.

> Does the compiler gain a large benefit from not having to include this feature? Who loses by allowing users to do what they want?

FWIW, I feel precisely the opposite as you. I'd rather have an ecosystem of code where top level functions must be annotated by types than an ecosystem where types are there only if the author feels like adding them. There is a small cost but a large gain, IMO.

reply

MichaelGG? 3 days ago

link

Oh if nested functions don't need annotation, then I suppose that saves most of the problem.

Can top-level definitions be of the lambda form? If not, what's the reason to have separate ways?

reply

burntsushi 3 days ago

link

There are `fn` types---which are just function pointers---and there are `

` types, which are closures that correspond to a pointer to a function and an environment.

You can actually define `fn`'s within other `fn`'s, but their types are not inferred. Closures cannot be defined at the top level, presumably because there is no global environment to capture. (Rust does have global "static" constants, though, which are available everywhere in the enclosing module I think.)

I can probably hazard a few guesses at why there is a split here (probably relating to lifetimes), but I don't know for sure. Perhaps someone else will chime in.

reply

MichaelGG? 3 days ago

link

But if a closure doesn't capture any variables then how it is different?

reply

smosher_ 2 days ago

link

The immediate difference is the body of what you need to infer against. In the local case it's quite small, and the compiler doesn't have to worry about a lot of what-ifs.

Personally I like seeing types and I'm glad people are forced to write them. (This is an opinion I've had long before Rust existed, so I'm not rationalizing excuses.)

reply

losvedir 3 days ago

link

> Who loses by allowing users to do what they want?

Everyone else who looks at your code who would have preferred them.

I dabble with both Go and Haskell, and this seems like the best of both worlds: from Go they enforce a uniform standard across libraries, coworkers' code, etc, and from Haskell, they're adopting the philosophy that "types are documentation".

I, too, would be a little annoyed at documenting lambdas, like this, but I think it's eminently reasonable to require it for all top-level function definitions. And it sounds like from another comment here, that that's the case. :)

reply


http://science.raphael.poss.name/rust-for-functional-programmers.html


bjz_ 3 days ago

link

> But what I got instead was an ML with some low-level extensions; it felt like an enormous bait-and-switch, as nobody is interested in yet-another-functional-language.

Rust is not functional. It may draw heavy inspiration from statically typed FP, and closures, ADTs, pattern matching, and and expression-heavy programming style might give that impression, but it is at its heart a procedural systems language. As stated in the blog post, most of Rust's core features map directly to underlying machine instructions, and there is always the opportunity to cede control from the type system if you absolutely have to. Indeed, core library types like `Box<T>` and `Vec<T>` are at their fundamentally built on `unsafe` code.

reply

jfager 3 days ago

link

What do you mean by 'low-level extensions'? There's nothing in the language proper that can't run on bare metal, how much lower can you get?

If anything, it's the functional parts that feel bolted on: closures are crippled (though getting better soonish), the types that get spit out of iterator chains are hideous, no currying, no HKTs, functional data structures are much harder to write w/o a gc, etc.

reply

---

pnathan 3 days ago

link

I've had a vastly happier Rust experience than C++ experience (I've written in both, well beyond the "zomgz 50line starter program").

The Rust compiler is vastly smarter and gets you type checking you have to pay out the nose for in C & C++. I'm a fanboy of Rust, but I would suggest looking hard at Rust for any C or C++ production code going forward. (My default going forward for this space will be Rust unless overriding considerations say otherwise).

reply

---


rubiquity 3 days ago

link

> Green threading: We are removing support from green threading from the standard library and moving it out into an external package.

I only ever looked at Rust from a 500 foot view while toying with it at a Hackathon, but I had no clue it had so many different types of threading models. This seems like a step in the right direction, indeed. If Task is going to your unit of concurrent execution, as much transparency around that as possible is a good thing.

reply

hornetblack 2 days ago

link

Green threading used to be default. And used libuv for io. Then native threading was avaliable. Then it became default. Now green threading is being moved out of builtin libs.

Starting with green threads is really easy too:

    extern crate green;
    extern crate rustuv;
  1. [start] fn start(argc: int, argv: *const *const u8) -> int { green::start(argc, argv, rustuv::event_loop, main) }
    fn main() {
        // this code is running in a pool of schedulers all     powered by libuv
    }

Edit: You can also start up a new libgreen scheduler pool at any time and add some native threads to the pool. So you can have some threads running with libgreen and some with libnative. (So you could theoretically embed a go program inside a rust program)

reply

masklinn 2 days ago

link

It had just two: libgreen backed tasks with "green threads", libnative backed them with OS threads.

reply

rubiquity 2 days ago

link

Does that mean every task will be an OS thread? Or are tasks handled by a runtime specific scheduler and will use a pool of threads for IO?

reply

masklinn 2 days ago

link

> Does that mean every task will be an OS thread?

By default, yes.

reply

rubiquity 2 days ago

link

Hmmmm, I hope there are other alternatives in the future (besides libuv). It would be nice to see Rust have a lightweight unit of execution similar to goroutines or Erlang processes.

reply

---

doe88 3 days ago

link

What I think is an important feature of this language is the ease with which it can interact with other languages. Especially the possibility for Rust code to be called from foreign languages such as C very easily.

I'm looking forward for even better support of iOS with the support of arm64, I think it is really important to offer an alternative.

BTW is there an RFC on dynamically sized types? I can't find any, I'm looking to learn of it works.

reply

xngzng 3 days ago

link

Agreed. Two areas I can think of: 1. Rust library that is callable from Python. Many are using C/C++ for optimised Python program. 2. Able to create iOS framework library in Rust to be callable from Swift/Objective-C; similarly Rust library callable from Android NDK.

reply

steveklabnik 3 days ago

link

> Especially the possibility for Rust code to be called from foreign languages such as C very easily.

The second production deployment of Rust is a Ruby gem, written in C, that calls out to Rust. It's used in skylight.io, if you're curious.

> BTW is there an RFC on dynamically sized types?

IIRC, DST was before the RFC process even existed, it's just taken forever to implement. The Duke Nukem Forever of Rust. :) http://smallcultfollowing.com/babysteps/blog/2014/01/05/dst-... is what you want to read, IIRC.

reply

wycats 3 days ago

link

> The second production deployment of Rust is a Ruby gem, written in C, that calls out to Rust. It's used in skylight.io, if you're curious.

Yep! I'm one of the authors of that project. The fact that Rust provides automatic memory cleanup and the attendant safety without runtime overhead (even ARC has non-trivial runtime overhead) was a huge win for us, as was the transparent FFI.

We were looking for a way to write fast code that was embeddable in a Ruby C extension with minimal runtime overhead and without a GC (two GCs in a single process is madness). We also wanted some guarantees that we wouldn't accidentally SEGV the Rails apps we were embedded in. Even last December, Rust was a clear winner for us.

We've been shipping Rust code to thousands of customers for many months, and given the language instability, it's worked really well for us.

reply


...

I'm looking to learn about how Rust's refcounting memory management works (and how it differs from how, e.g. Objective-C or Swift's runtime-based reference counting works), mostly for personal edification. Can anyone point me to any good resources?

reply

burntsushi 3 days ago

link

Steve already told you that refcounting isn't reached for by default, and I wanted to share an anecdote to emphasize that.

I've written a regex library, CSV parser, command line arg parser, elastic tabs and quickcheck in Rust. Behold:

    $ find ./ -type f -name '*.rs' -print0 | xargs -0 grep Rc
    $

I've definitely reached for it a few times as an escape hatch, but I've always ended up finding a cleaner approach to persist without it.

Of course, there are plenty of legitimate uses of refcounting. I just haven't hit them yet. :-)

reply

steveklabnik 3 days ago

link

You're logged in as root? O_O :p :)

reply

burntsushi 3 days ago

link

Haha, no, I added that in after-the-copy-and-paste. :P

Fixed nonetheless. Do'h.

reply

steveklabnik 3 days ago

link

Is worth noting that you don't reach for reference counting by default in Rust: your reach for references, then boxes, THEN Arc.

You can find documentation on these types here: http://doc.rust-lang.org/guide-pointers.html

reply

eddyb 2 days ago

link

Well, you wouldn't go for Arc (atomic RC) unless you need to share data across tasks (threads), within the same task you can use Rc.

This is different from shared_ptr in C++ which is always atomic and LLVM has to try hard (in clang, no idea what GCC does) to eliminate some redundant ref-counts.

Oh and since Rc is affine, you only ref-count when you call .clone() on it or you let it fall out of scope.

Most of the time you can pass a reference to the contents, or, if you need to sometimes clone it, a reference to the Rc handle, avoiding ref-counting costs.

reply

steveklabnik 2 days ago

link

That's true, thanks.

reply

---

sriku 3 days ago

link

This is all good. No higher kinded types for v1.0?

reply

steveklabnik 3 days ago

link

Nope. We're pretty sure they'll be backwards compatible, and there are too many other outstanding issues (eg, syntax) and too little time. Lots of us want them though!

reply

spott 3 days ago

link

There isn't an RFC for them, at least that I could find. Have they just not gotten that far in terms of thought?

reply

TheHydroImpulse? 3 days ago

link

I have an RFC sitting here (and a few blog posts talking about it), but considering it'll be post 1.0 when such a thing is even entertained, I haven't submitted it. There are more pressing and more appropriate things to work on first.

reply

bjz_ 3 days ago

link

Awesome - looking forward to seeing that. :)

reply

jroesch 3 days ago

link

I'm pretty sure at least a few people have ideas/are working on them. I have a 75% finished RFC that I have been sitting on because the language has been in such flux lately. I wouldn't be surprised if Niko, Aaron or someone else on the core team doesn't already have strong ideas about them.

reply

steveklabnik 3 days ago

link

That's right, there hasn't even been enough work for an RFC.

reply

jroesch 3 days ago

link

I think one of the core challenges is how traits play with HKTs. You can only allow for the defining of HKed type parameters and type arguments so many ways, but it becomes more complicated when dealing with the resolution of traits around HKTs. In Haskell for ex. there is no `Self` so the type class can easily be dispatched on a higher kinded type without needing a receiver type. In Rust `Self` must currently be fully applied as it is resolved from a value directly: `value.trait_method` and we pick our instance based on value's type. If we want to implement Functor (Mappable or w/e) we need to come up with another approach. I've thought about a few but they seem to play best with multi-dispatch + associated types + where clauses. I'm hoping to finish the RFC once all these features land and I can actually hack a prototype, instead of scribbling on paper.

reply

---

3289 3 days ago

link

It seems that 1.0 is going to be a solid release. But the post-1.0 Rust is going to be even more exciting once they have added inheritance and subtyping which enable true polymorphic reuse!

reply

strcat 2 days ago

link

Object inheritance is only useful in rare edge cases so your statement doesn't make much sense. Traits have default methods, inheritance and can be used as bounds on generics (no dynamic dispatch, type not lost) or as objects (dynamic dispatch / type erasure).

What makes you think object inheritance is such a sure thing anyway? I don't expect either object inheritance or optional garbage collection to materialize, ever. In fact, I'd be pretty sad if the language was degraded with that extra complexity - I think it would be a worse situation than the mess that is C++. There would no longer be a shared idiomatic Rust.

reply


" Where is Rust Positioned?

The three languages where I do most of my work in are Python, C and C++. To C++ I have a very ambivalent relationship because I never quite know which part of the language I should use. C is straightforward because the language is tiny. C++ on the other hand has all of those features where you need to pick yourself a subset that you can use and it's almost guaranteed that someone else picks a different one. The worst part there is that you can get into really holy wars about what you should be doing. I have a huge hatred towards both the STL and boost and that has existed even before I worked in the games industry. However every time the topic comes up there is at least someone who tells me I'm wrong and don't understand the language.

Rust for me fits where I use Python, C and C++ but it fills that spot in very different categories. Python I use as language for writing small tools as well as large scale server software. Python there works well for me primarily because the ecosystem is large and when it breaks it's a super straightforward thing to debug. It also keeps stays running and can report if things go wrong.

However one interesting thing about Python is that unlike many other dynamic languages, Python feels very predictable. For me this is largely because I am a lot less dependent on the garbage collector that in many other languages. The reason for this is that Python for me means CPython and CPython means refcounting. I'm the guy who will go through your Python codebase and break up cycles by introducing weak references. Who will put a refcount check before and after requests to make sure we're not building up cycles. Why? Because I like when you can reason about what the system is doing. I'm not crazy and will disable the cycle collector but I want it to be predictable.

Sure, Python is slow, Python has really bad concurrency support, the interpreter is quite weak and it really feels like it should work differently, but it does not cause me big problems. I can start it and it will still be there and running when I come back a month afterwards.

Rust is in your face with memory and data. It's very much like C and C++. However unlike C and C++ it feels more like you're programming with Python from the API point of view because of the type inference and because the API of the standard library was clearly written with programmer satisfaction in mind.

...

If you try, you can still make terrible decisions for concurrency in Rust, but you really need to go out of your way to do it. The language forces you to think more and I believe that's a good thing. I don't want to become another "objective oriented programming is the billion dollar mistake" preacher but I do think that the language decides largely what code people write. Because subclassing is easy in C++, Java, Python and many more languages this is what we write. And then birds are instances of animal classes. If you take that tool away you start thinking differently and that's a good thing. CPUs stop getting faster and looking at one object at the time really no longer makes any sense at all. We need to start reasoning a lot more about collections of things and what transformations we actually want to do.

...

Rust is inspiring for many reasons. The biggest reason I like it is because it's practical. I tried Haskell, I tried Erlang and neither of those languages spoke "I am a practical language" to me. I know there are many programmers that adore them, but they are not for me. Even if I could love those languages, other programmers would never do and that takes a lot of enjoyment away.

...

Rust is something that anyone can pick up and it's fun all around. First of all (unless you hit a compiler bug) it won't crash on you. It also gives you nice error messages if it fails compiling. Not perfect, but pretty good. It comes with a package manager that handles dependencies for you, so you can start using libraries other people wrote without having to deal with a crappy or non existing ecosystem. Python got much better over the years but packaging is still the biggest frustration people have. Cargo (rust's package manager) is barely half a year old, but it has a full time developer on it and it's fun to use.

Even just the installation experience of the language is top notch. It gives you compiler + documentation tool + package manager and you're good to go. The fact alone that it has a documentation tool that spits out beautifully looking documentation out of the box is a major contributor to enjoying programming in it. While I wish it had a bit more of Sphinx and a bit less of javadoc, it's a really good start for more.

But what's really inspiring about Rust are the small things. When I first played with Rust what amazed me the most was the good FFI support. Not only could you call into C libraries easily: it also found and linked them for you. There is so much more though and it's hidden everywhere. There is a macro (include_str!) that will read a file next to your source at compile time into a string into your binary (How cool is that!?). Not only can you bake in contents of files, you can also pull environment variables into your binaries for instance. " -- http://lucumr.pocoo.org/2014/10/1/a-fresh-look-at-rust/

---

ekidd 22 hours ago

link

I've been using Rust to process a large text corpus, and as Armin suggests, it's a really interesting experience. Here are a few things I've noticed so far:

1. Writing Rust code definitely takes more time than Python or Ruby, but it's not bad in practice. I can't measure the productivity difference yet, partly because I'm still learning Rust. I do spend more time thinking about how to write zero-allocation and zero-copy algorithms, and trying to get my code to compile (but see 3, below).

2. It's possible to write very fast code without compromising memory safety. It's great to see key parsing operations happening in ~100 nanoseconds, with no allocations.

3. Rust is one of those languages where I need to work to make the compiler happy, but once I manage that, the code generally works on the first try.

4. The tooling is very good: library management, unit testing, benchmarking, documentation, error messages etc., all work very nicely.

Overall, it lacks the instant gratification of a really good scripting language. But I keep finding myself back in Emacs, writing more Rust code for the fun of it. And I really enjoy the combination of low-level code, memory safety, and a useful selection of functional programming features. I'm glad to see a new language in this niche.

burntsushi 21 hours ago

link

> Rust is one of those languages where I need to work to make the compiler happy, but once I manage that, the code generally works on the first try.

I can confirm this. I have a CSV parser[1] that is maybe twice as fast as Python's CSV parser (which is written in C)+. There's nothing magical going on: with Rust, I can expose a safe iterator over fields in a record without allocating.

[1] - https://github.com/BurntSushi/rust-csv

The docs explain the different access patterns (start with convenience and move toward performance): http://burntsushi.net/rustdoc/csv/#iteratoring-over-records

+ - Still working on gathering evidence...

reply

kibwen 22 hours ago

link

I adore that someone like Armin is so delighted to use Rust. From what I've seen of Flask and his other Python libraries, the level of consideration that he devotes to API design is absolutely inspiring.

At such an early stage in the language's history, I'm optimistic that his thoughtfulness in API design will become the baseline for the entire Rust ecosystem. In this vein, I'd also like to credit Chris Morgan's Teepee (http://chrismorgan.info/blog/introducing-teepee.html) and Sven Nilsen's Piston (https://github.com/PistonDevelopers) for devoting a great deal of energy to figuring out how best to structure Rust APIs that play to the strengths of the language. It's a process of discovery for all of us!

reply

dbaupp 22 hours ago

link

Don't forget Aaron Turon (employed by Mozilla to work on the design of the std library), who, among other things, wrote the "fluent" APIs for process[1] and task[2] spawning, which possibly inspired the redis-rs API Armin describes in this post.

[1]: http://doc.rust-lang.org/nightly/std/io/process/struct.Comma...

[2]: http://doc.rust-lang.org/nightly/std/task/struct.TaskBuilder...

reply

---

https://news.ycombinator.com/item?id=8392138

bsaul 15 hours ago

link

I found the python version of the code so much more readable... is putting "unwrap" and "ignore" everywhere becoming idiomatic in Rust ?

reply

the_mitsuhiko 14 hours ago

link

No, it's just in this example. In idomatic rust you would write this probably: https://gist.github.com/mitsuhiko/1f37c1092bdf54ce4213

The `try!` macro unwraps the okay part of the expression and early returns the err part. The nice thing about the explicit results is that you will do the error handling. With Python you often do not know if an error might come.

reply

bsaul 14 hours ago

link

Ok, much better. At the risk of looking more like a java try/catch, is it possible to wrap multiple statements in a single try! clause ?

Or at least have some way of saying " if there's any error in this block of code, simply stop execution and forward it to the calling statement"

reply

the_mitsuhiko 10 hours ago

link

No, but that's would also defeat the purpose. The idea of results is that you know from looking at the code what can fail. If you remove that information you might as well use exceptions.

reply

nixpulvis 21 hours ago

link

In addition to being a great lang, I love all the little things. Like he mentions, the doc tools and compiler are very nice.

reply

bjz_ 20 hours ago

link

> the [...] compiler [is] very nice.

No doubt clang has been a good influence in setting the standard for gorgeous, readable error messages with nice colored squigglies and arrows. Borrow check errors probably the hairiest you get, and you get less and less of those as you internalise the rules.

reply


TOREAD i haven't read the comments in this section but they look great:

MichaelGG? 3 hours ago

link

Hi! I use F# for most tasks (from websites/JS generation, to packet capture and indexing, call routing and billing processing), and some C where required. Rust looks fantastic, and would give me the memory control I need when I need extra performance. A LOT of it comes down to simply being able to stack-allocate things; in F# I'm essentially forced to use the GC heap for even the most trivial things.

Rust looks fantastic, and I'm very excited about using it. When I found out about it and started reading how it worked, it was almost exactly what I had been wanting, on almost every count. I really wish it had existed a few years ago.

My comments are from someone that's just been playing around with the getting started guide of Rust.

-- Lack of custom operators limits expressiveness. For instance, look at Parsec or FParsec. Why shouldn't they be allowed to exist in Rust? But if custom operators are totally out of the question, then what about user-defined infix functions? (And then, why not functions with nearly arbitrary codepoints as identifiers?)

-- It seems that currying, partial application, and function composition are sorta cumbersome in Rust. Is this a conscious decision, that idiomatic Rust shouldn't be doing such things? Like " add >> inc >> print " being equivalent to "

a,b-> print(inc(add(a,b)))" ? In F# I use this kind of stuff all the time, especially when processing lists.

-- It seems there's a difference between function types. Like if I do "fn foo(a:int, f:proc(int)->int)", I can call it with foo(1i, inc) if inc is a fn. But if I first do "let f = inc; foo(i1, f)", that's an error. Offhand, I'd assume this is due to lifetime management, but it feels a bit strange. When writing HoFs?, do I need to implement a version for each kind of function? Or am I totally misunderstanding things?

-- Sorta related, does Rust allow something like:

  let inc =
    let x = ~0
    || { let res = *x; *x += 1; res }

The idea is to expose a function that contains some private state. I remember hearing that Rust changed the ownership stuff around a few times, but the basic idea is to create a globally-accessible closure. Is this impossible, requiring us to use statics to store the global state?

-- Why doesn't Rust warn when discarding the result of a function if not unit? Is it idiomatic Rust to return values that are often ignored?

-- Even without higher kinded types, monadic syntax is useful. Is Rust planning any sort of syntax that'd let users implement Option or Async? How does Rust avoid callback hell? Or is this quite possible today with macros and syntax extensions?

-- Has Rust considered Active Patterns (ala F#)? With that, I can define arbitrary patterns and use them with matching syntax. E.g. define a Regex pattern, then "match s { Regex("\d") => ... , Regex("\D") => ... , _ => ... }"

-- Consider allowing trailing commas in declarations; why special-case the last item?

-- And last but not least: Please, please, please, reconsider type inference. It's baffling why "fn" requires type annotations, but a closure does not. What's more, why is there even different function syntax in the first place? I've heard the reason that "top level items should have types documented", but that's a very personal decision to make. It certainly isn't something the Rust compiler should force me to do in all my code. Why do ya gotta limit my expressiveness? (Same argument I'd use for custom operators.) Statics/consts should also have type inference. And note that these items aren't necessarily public or exposed - but a private fn on a module still requires type annotations.

reply

dbaupp 2 hours ago

link

> -- Consider allowing trailing commas in declarations; why special-case the last item?

They are allowed in most circumstances.

reply

kibwen 2 hours ago

link

And if they're not, I'd say that might be a bug.

reply

tomp 13 hours ago

link

I think that mutability should also be recorded in types (i.e. even a &mut would not allow you to modify an immutable type). Then, programmers could control whether variants/enums are "fat" (use the maximum amount of memory of any variant in the same type) or "thin" (use the minimum amount of memory for the given variant) by changing the mutability of fields they are stored in.

But I totally understand that you probably have other, well thought-out reasons not to do that.

reply

kibwen 11 hours ago

link

Off the top of my head this seems like it would allow the amount of memory taken up by an enum to vary dynamically, which would prohibit stack allocation and require heap allocation instead. It would also be impossible to realize this optimization for any array of enums, since they would all need to be the same size for efficient indexing, and further it would wreak havoc with structs containing enums by making it impossible to statically compute field offsets.

However, we have considered elevating the position of mutability in the type system for other reasons--specifically, to make it possible to write a function that is generic with respect to mutability. But there's never been a huge amount of enthusiasm for that, so I wouldn't expect any changes of that sort.

But thanks for the feedback anyway! :) It's definitely valuable to know that people do care about the size of enums. We have various optimizations required by the language spec to shrink them down wherever we can do so while preserving semantics, and we welcome people to concoct more.

reply

tomp 10 hours ago

link

Well, it would definitely need to be special-cased, i.e. only use it when (1) the field containing the enum is immutable, and (2) it's the last field in the structure (it would then make the structure a Dynamically Sized Type).

I only care about the size of enums inasmuch as I find it wasteful to allocate a 10 words long Box to hold a 2 words long enum. Of course, the optimization would only make sense if the Box was immutable (even with a unique reference). I mostly mentioned the enum size issue because I read somewhere not too long ago that Servo people were having problems with memory usage because of that (and that they also want objects/records with inheritance of fields - that would bring all the issues you speak about above).

reply

kibwen 8 hours ago

link

It's true, enum design in Rust can be a fine art. Specifically, knowing when to make the tradeoff between big inlined enum variants and pointer-sized variants that require an indirection to access. This plagued the Rust compiler for a long time as well, as the AST was formerly represented by a ludicrously large enum with something like 120 words per node.

reply

yen223 15 hours ago

link

I've been using Rust for toy projects for a while, and I gotta say Rust is one of the nicest languages out there. Kudos to the Rust team.

As a relative newbie to Rust, one of the biggest hurdles I faced was that of poor documentation. A lot of "rust xxx" searches would link to outdated articles or to stale links in the Rust official docs. I understand that Rust is a young language, and documentation is probably the last thing on the core devs' to-do list (and rightly so), but I think it would greatly help drive adoption rates if we could proper docs in place.

reply

masklinn 14 hours ago

link

> A lot of "rust xxx" searches would link to outdated articles or to stale links in the Rust official docs.

Or to an old mirror of the official doc, http://web.mit.edu/rust-lang_v0.9 is the bane of my rustperience.

reply

steveklabnik 4 hours ago

link

I specifically contacted MIT about this issue, and unfortunately, we're just gonna have to out-SEO them.

reply

saosebastiao 21 hours ago

link

Two things that come to my mind: 1) General concurrency/parallelism ease of use. It would be nice to have Async/Await a la C#, as well as some easier to use channels and synchronization primitives/functions along the lines of Go or Core.Async. And 2) use of <> for type annotations instead of [], which is much easier to parse when nested.

reply

kibwen 21 hours ago

link

Servo is definitely demanding a wide variety of approaches to both concurrency and parallelism, so I expect the facilities for such in Rust to mature very quickly (but faster with more help and feedback!). It's a very important issue that is seeing work from full-time developers as we speak.

As for the second point, we've gone beyond the bikeshed (<> and [] are both exactly as ambiguous to the grammar, so it's just an argument of preference by this point) and addressed the root issue with `where` clauses, which prevent you from needing to nest <> at all for a majority (or perhaps plurality, I haven't measured) of function signatures where you previously needed to.

reply

Symmetry 8 hours ago

link

The ability to evaluate functions at compile time would be really nice. I was sort of looking at writing a macro in Rust to mirror Nimrod/Nim's 'when' statements but gave sort of gave up when I saw that. But of course that's probably an entirely unreasonable thing to request in the remaining time you have and it might be incompatible with having a borrow checker anyways.

The borrow checker looks really valuable though, and something that might have a big influence on language design going forward.

reply

kibwen 8 hours ago

link

Lightweight compile-time evaluation is definitely a weakness of ours. A full-fledged syntax extension can get you pretty far, but those are a terror to write and ugly to import. While I don't know of any proposals in the air at the moment, I expect the developers will treat this with great importance post-1.0.

reply

saosebastiao 7 hours ago

link

Interesting. From what I understand, this was what BurntSushi?'s regex macro was intended to accomplish: an efficiently compiled regex that optimized away all unneeded functionality for regex literals. With CTFE, would this get rid of the need for a macro entirely?

reply

burntsushi 1 hour ago

link

The regex macro has two primary benefits:

1. You cannot compile your program with an invalid regex.

2. The regex runs in a specialized VM corresponding to the specific bytecodes for a regex. This results in an across-the-board performance improvement, mostly because there are some places that can now use stack allocation instead of heap allocation. (But it's still a full NFA simulation, so it isn't going to, say, beat RE2/C++ quite yet.)

You can take a look at the syntax extension here: https://github.com/rust-lang/rust/blob/master/src/libregex_m...

Basically, if CTFE allows you to write functions that produce AST transforms, then the `regex!` macro should be convertible to that form. Most of the syntax extension is a quasiquoted copy of the general VM: https://github.com/rust-lang/rust/blob/master/src/libregex/v... --- The actual syntax extension bits are pretty minimal.

(There are quite a few tricks employed to get native and dynamic regexes to have the same API. It's the reason why the main `Regex` type is actually an `enum`! https://github.com/rust-lang/rust/blob/master/src/libregex/r...)

Full disclaimer: the `regex!` macro does have a couple downsides. #1, it's a syntax extension, so it has a dynamic dependency on `libsyntax`. #2, it ends up producing a lot of code and bloating your binary if you use it a lot (you might start noticing at after the first few dozen, and you might start cursing it after the first few hundred).

reply

kibwen 6 hours ago

link

Sadly I'm not enough of an expert to say for sure. It would certainly depend on the specific form that it took, and there are a multitude of options in this space. Of the current contributors who are willing and able to push the boundaries here, I believe that quasiquotation is the preferred approach (motivated by use cases for compile-time codegen in Servo).

reply

burntsushi 1 hour ago

link

> I believe that quasiquotation is the preferred approach

Oh yes yes yes. Quasiquotation makes writing syntax extensions almost easy.

It can't cover everything though. For those cases, dropping down into AstBuilder? (or the raw AST) isn't completely terrible, but it's a big step down from quasiquoting.

reply

rz2k 19 hours ago

link

Over the past few days I have been trying Julia. While I don't have much of an idea what I'm doing yet, and I'm probably doing a lot of unproductive premature optimization for the sake of exploring, I see a similar type of difference compared to R and Python where I have more experience.

What I am curious about is why you chose Rust rather than Julia. As I've understood the chatter so far, Rust is great for low level programming, and can work well as a replacement for other system level programming languages. Julia on the other hand is supposedly tailored for the type of task you are talking about, and like Rust, the chatter says Julia can be similar to C and Fortan in terms of speed.

My understanding of Rust was as a low-level general-purpose language and Julia as a technical or scientific programming language that is similarly fast.

Anyway, I'm just curious about the trade offs.

reply

callcongressnow 18 hours ago

link

As far as I am aware, Julia isn't that fast for string operations yet. The language has been optimized for numerical calculations but they haven't done much work on making working with strings as fast as they could. I remember reading this on a github issue at some point but I can't find the issue at the moment.

reply

"

    Rust is a safe, concurrent, practical, curly-brace, block-structured expression language. It supports a mixture of imperative procedural, concurrent actor, object-oriented and pure functional styles. Rust also supports generic programming and metaprogramming, in both static and dynamic styles.

Rust is backed by Mozilla. In some of my previous posts I already mentioned my exciteness about Rust. Not long ago Rust v0.1 has been released so I had the chance to play a bit with it. I actually implemented quite a bit of the aforementioned project and it was fun. It felt like the perfect blend of an imperative language like C and a functional one like Haskell or OCaml while still feeling really lightweight. I especially like the structural records, interfaces (like type classes in Haskell) and the separation of namespaces without overloading the .-operator. Rust also has some more or less unique features regarding the control of memory layout, even though I did not make much use of them I acknowledge them to be an advantage.

...

Conclusion and comments

Again, all these listed languages are not in a production ready state yet but I’m going to check back in approximately six months and I hope that by then most of the rough corners will have been polished. I think it obviously came across that I like Kotlin and Rust best and so naturally I hope that these will be extra polished by then. " -- http://www.i-programmer.info/professional-programmer/i-programmer/3892-the-functional-view-of-the-new-languages.html?start=2

---

"Python had been my favourite language for about five years, but although I still work in Python professionally (I’d enjoy changing that to Rust, by the way) I find myself missing features from Rust more and more, and it really hasn’t worked the other way except with regards to libraries that haven’t been written in Rust yet. Rust has really spoiled Python for me. " -- http://chrismorgan.info/blog/rust-fizzbuzz.html

---

http://chrismorgan.info/blog/rust-fizzbuzz.html

toread: http://chrismorgan.info/blog/rust-fizzbuzz.html

---

https://medium.com/@adamhjk/rust-and-go-e18d511fbd95

---

http://blog.rust-lang.org/2015/01/09/Rust-1.0-alpha.html

---

http://arthurtw.github.io/2014/12/21/rust-anti-sloppy-programming-language.html

--

ilitirit 8 hours ago

I like Rust, and I'm probably missing something, but do I feel that some things should be easier. At times the language (or standard library, I suppose) feels a bit unwieldy. For example, trying to coerce a string from certain types:

    let path = Path::new(&app_path)
    println!("file name: {}", path.file_name().unwrap().to_str().unwrap());

Hopefully this is just a case of unfamiliarity, but surely something like should be easier?

reply

Jweb_Guru 7 hours ago

If you were willing to allow for potential escape characters and acknowledge (to the user) the fact that the path might not have a file name, you could write:

    println!("file name: {:?}", path.file_name());

or just for escape characters, but only if the file name existed:

    if let Some(name) = path.file_name() {
        println!("file name: {:?}", name);
    }

Note that the unwrap() isn't boilerplate, in either case: if you are using `unwrap()` you are implicitly saying that you know for a fact that the file name is present (the first time) and a valid UTF-8 string (the second time), which isn't guaranteed in the general case on all platforms. If you wanted to write this more robustly, and these weren't guaranteed, you would need to explicitly take care of those cases.

I/O has lots of tricky error cases, and Rust usually requires you to acknowledge that. You can avoid dealing with those cases with unwrap(), or you can explicitly handle them. Personally, I much prefer this to being surprised only when the weird error case unexpectedly turns up because I never considered the situation in question, and that's generally how Rust handles things, but it depends on the task. It may make Rust less appropriate for quick scripting tasks, for example, where the domain is well-defined and the script is not likely to be reused (but I find that those scripts have an annoying way of getting reused anyway).

reply

drchickensalad 5 hours ago

However, rust would be relatively well-suited for a "dirty quick scripting task". You could make a more traditional wrapper for the operations you like to use that assumes these rare edge cases don't happen, and panic otherwise.

This is sort of rust's philosophy; being explicit. Rust has no problem with failing, it just has a problem with failing without you telling it to. If you write a wrapper that implies you are willing to fail on these cases, then you're being explicit by choosing to use these functions instead :)

reply

masklinn 6 hours ago

> surely something like should be easier?

That would be nice, but as others hinted that wouldn't actually work:

The Rust philosophy is generally to expose these issues upfront and force the developer to consider them (and either handle or explicitly ignore them). It is indubitably less convenient than e.g. Python 3's `PurePath?(app_path).name` returning an str, but you'll have had to decide what happens in case of failure.

Paths are really a good example of something which looks simple, should be simple, and is actually anything but simple for historical and technical reasons, especially in a cross-platform context.

[0]

reply

WallWextra? 5 hours ago

Also, OSX applies unicode normalization to the filenames, which can cause some interoperability problems with unix, e.g. you try to copy files forth and back between the systems and end up with two copies of a file, with identical-looking filenames that differ in their unicode normal form.

reply

Manishearth 7 hours ago

To add to the other comments, Rust prefers to make error cases explicit. We have memory safety. You can opt out of it, but you have to opt out explicitly. We have exhaustive match. You can use a wildcard, but you need to explicitly do so. We have nullables and Results. You need to explicitly acknowledge the error/null case, or use `unwrap()` to say that you don't mind a panic at this point.

This leads to verbosity at times, but usually its a good verbosity.

reply

mrec 3 hours ago

I'm sure I'm not the first (or the hundredth) to say this, but `unwrap` has always struck me as a horribly innocuous spelling of "yes, feel free to crash here".

I presume there is (or could be) a standard lint to prevent this getting into production code, but it still feels kind of icky.

reply

steveklabnik 2 hours ago

We almost changed it to `assert` but that made some people _really_ upset.

reply

steveklabnik 7 hours ago

Yes, it's true. Code like this is going to be more verbose. Often, it's due to Rust being a systems language and exposing low-level details to you, combined with the type system ensuring safety. In this instance, `Path` is actually fairly complex. From the RFC: https://github.com/rust-lang/rfcs/blob/master/text/0474-path...

        > The design of a path abstraction is surprisingly hard. Paths work
        > radically differently on different platforms, so providing a
        > cross-platform abstraction is challenging. On some platforms, paths
        > are not required to be in Unicode, posing ergonomic and semantic
        > difficulties for a Rust API. These difficulties are compounded if one
        > also tries to provide efficient path manipulation that does not, for
        > example, require extraneous copying. And, of course, the API should
        > be easy and pleasant to use.

So, in this case, you have to call `file_name` to get the filename, but that returns an `Option`, since it might not be a file, it could be a directory. The `unwrap` says "crash if it's not a filename." You then have an `OsStr?`, which is a string type that handles the kinds of cross-platform issues alluded to in the above paragraph. `to_str` will then attempt to turn it into a `&str`, but because `&str` must be unicode, if the conversion fails, it _also_ returns an Option, which is the last unwrap.

In systems programming, details matter. As such, our stdlib APIs try to expose those details. I expect some people will write higher-level abstractions that hide all this if you don't care as much.

Tradeoffs tradeoffs tradeoffs...

reply

jerf 2 hours ago

"Often, it's due to Rust being a systems language and exposing low-level details to you, combined with the type system ensuring safety."

I'm not convinced it's being a "systems language" (in the sense I think you mean). The last three languages I've learned to fluency have all, in various ways, been very concerned about errors: Erlang, Haskell, and Go. Yes, they have widely varying opinions about what to do with them, but they all agree that you have to be fairly constantly worried about them. You can't sweep them under the rug; you need a strategy, and you need to deal with the resulting issues head on.

Despite their differences, what I feel like I've learned from them is that historically speaking, the vast majority of popular languages and their communities have been downright cavalier about errors. It's often in all the little details rather than a consistent, stated design goal, but the presence of various nils and nulls, APIs that will happily return empty strings for paths instead of errors, and all kinds of APIs and libraries that seem to be subconsciously written with the belief that returning an error is a failure on the part of the library and will go to what now seem to me to be absurd lengths to somehow keep klunking along in the presence of already-manifested failure. Even for languages that think they're being really good with exceptions, I find that when I go back to them, they end up using the exception support to "permit" themselves to be sloppy and hope the exceptions will pick up the pieces... and they often don't. Not because of some theoretical weakness, but because allowing developers to think they don't have to think about the error cases means precisely that they don't!

People are used to errors not poking them in the face until deploy time. It seems to me there's a strong, broad motion towards developing languages that will in one way or another force you to deal with them at compile time. It's a culture shock for people, but, I'll tell you this, get really good at Rust or any of these other languages and go back to any of those other languages, and you will find your code changes in those other languages to suddenly look a lot more like your Rust does anyhow, which means this putative disadvantage of Rust evaporates anyhow. Once you see how sloppy you were being, it's hard to go back in good conscience.

reply

RickHull? 7 minutes ago

Great comment. I have run into this problem exactly in inherited codebases at my last couple of jobs:

> libraries that seem to be subconsciously written with the belief that returning an error is a failure on the part of the library and will go to what now seem to me to be absurd lengths to somehow keep klunking along in the presence of already-manifested failure.

It's a big problem with hairy repercussions if this idea is allowed to take root.

reply

steveklabnik 2 hours ago

> The last three languages I've learned to fluency have all, in various ways, been very concerned about errors

Yeah, maybe I should revise my statement a bit: systems languages, overall, tend to expose more details. I meant this in a broader context than errors. As a simple example, manual memory management exposes more details than GC. There are many languages that care dearly about errors, as you've rightfully pointed out.

I broadly agree with the rest of your post. Error handling is hard and tedious, and programmers don't like doing it. I myself had an embarrassing bug in `rustbook` where I did `let _ =`. I only intended to start off that way! Programming is hard. But using Haskell, Rust, and other languages like this have certainly changed my code in other languages.

reply

phaylon 7 hours ago

I would say it is familiarity, but in another sense. You can use something like

    path.file_name().and_then(|f| f.to_str()).unwrap()

but that really doesn't buy you much.

Where familiarity comes in I believe is in what that line communicates to me: There might not be a file name (like having '/' as path), and that getting a string slice out of it might fail (the kind of string the platform uses for paths might not be UTF-8 compatible).

Plus, the unwrap()s tell me that whoever wrote the code considered it an assertion failure if one of those is happening.

I read a lot more Rust than I write, and it really helps that the code communicates a lot. And with knowing about OsString?/OsStr? beforehand, I only had to lookup when file_name() returns None, the rest is rather implicit by convention, or familiarity.

reply

tikue_ 2 hours ago

I wish `and_then` were named `flat_map` so badly.

reply

doomrobo 7 hours ago

That would be:

    let path = Path::new(&app_path);
    println!("file name: {}", path.display());

reply

ilitirit 7 hours ago

No, I just want the file name.

reply

--

Mr_T_ 8 hours ago

The 'String', '~str' dichotomy is really unfortunate.

reply

pcwalton 7 hours ago

Presumably you mean String/&str? (~str hasn't existed for a long time.)

On the contrary, the lack of the string/string-view distinction is widely considered a flaw in the C++ standard libraries. C++14 introduces std::string_view to correct it. Rust's String/str distinction is just the same as C++14's std::string/std::string_view.

reply

frowaway001 7 hours ago

Apart from the Rust's usual rule to invent some abbreviations, but only apply them half the time.

Then "types start with an uppercase letter" except when the language creators break their own rule.

Then the fun "sometimes we use () and sometimes we use [] to do practically the same thing".

Then the various insanities with ::.

Then Generics. How many examples of languages which tried to use <> for Generics do we actually need before people learn the lesson?

I really wished some people took Rust, and wrote a new frontend to get rid of all the pointless inconsistencies and other "C did, so it has to be good"-isms (like_all_this_underscore_stuff or the ridiculous abbreviations), because many ideas of Rust are not only good but brilliant and deserve to be put into languages which don't belong to the purely-academic category.

I really wish Rust to succeed, but can we stop this approach of "3 steps forward and 2 steps backward" to language design?

reply

pcwalton 7 hours ago

> Apart from the Rust's usual rule to invent some abbreviations, but only apply them half the time.

Do you have an example?

> Then "types start with an uppercase letter" except when the language creators break their own rule.

They always start with a capital letter, except for built-in types or FFI bindings to libraries such as libc where the original types used a lowercase letter. This convention is exactly the same as Java.

> Then the fun "sometimes we use () and sometimes we use [] to do practically the same thing".

I've never heard this before. Do you have an example? () is used for function calls, grouping, and tuples, while [] is used for array literals and array indexing.

> Then the various insanities with ::.

The double-colon is very useful so that you can tell the difference between module lookup and field projection/method calling. Early Rust used . for module lookup, and it was confusing to tell at a glance whether a function was being called or a method was being invoked.

> Then Generics. How many examples of languages which tried to use <> for Generics do we actually need before people learn the lesson?

Using square brackets like Scala wouldn't actually help with the ambiguity, because it would be ambiguous with array indexing. The only syntax I've seen that actually solves the ambiguity is D's `!()`, which was deemed too unfamiliar. Angle brackets were chosen because they are familiar and aren't really any worse than the other mainstream options.

> I really wished some people took Rust, and wrote a new frontend to get rid of all the pointless inconsistencies and other "C did, so it has to be good"-isms (like_all_this_underscore_stuff or the ridiculous abbreviations)

The underscore naming convention was actually taken from Python's PEP 8. Rust doesn't really have any more abbreviations than most other languages at this point.

reply

callahad 6 hours ago

> > Then the fun "sometimes we use () and sometimes we use [] to do practically the same thing".

> I've never heard this before. Do you have an example? () is used for function calls, grouping, and tuples, while [] is used for array literals and array indexing.

Maybe referring to the ability to use either type of brace with macros?

reply

pcwalton 6 hours ago

Yeah, you can use any type of delimiter with macros, but I think that's an important feature, especially since macros can expand to declarations. Early versions of Rust required parentheses for all macro invocations, and that looked really ugly for anything that looked like a block. Delimiter interchangeability is a feature from Racket that works really well when it comes to macros and I'd hate to give it up.

reply

blaenk 5 hours ago

Agreed. They allow you to naturally create vectors with the bracket delimiter `vec![1, 2, 3]`, create spawn-like macros with the brace delimiter `spawn { code }`, and in the normal case just use parentheses `println!("testing")`

reply

alricb 2 hours ago

What about '{}'? It's a nice pair of opening/closing characters.

reply

frowaway001 3 hours ago

Considering the glorious downvoting, I won't bother comment any further except saying that these "issues" Rust devs keep pointing out have been addressed.

And now, I won't waste any further time with this cult.

reply

Daishiman 26 minutes ago

It's so sad, because I yet to see a single place where they're addressed.

reply

mbrubeck 7 hours ago

In this example the dichotomy is between String (which is guaranteed by the type system to be valid UTF-8) and OsStr? (which might be in an unknown encoding or otherwise not decodable to valid Unicode).

This is exactly when you want a systems language to require explicit conversions, rather than converting things silently and possibly losing or corrupting data.

reply

davvid 6 hours ago

rather than converting things silently and possibly losing or corrupting data

Exactly. Python3 went down the "silently converting" route, and it's not pretty[1]. I would go so far as to call it harmful.

http://lucumr.pocoo.org/2014/5/12/everything-about-unicode/

I understand the difficulty in this space; much of it is caused by forcing the Windows unicode filesystem API onto python as its world-view, rather than sticking to the traditional Unix bytes world-view. I'm unixy, so I'm completely biased, but I think adopting the Windows approach is fundamentally broken.

reply

Veedrac 5 hours ago

The problem there is overblown - it's basically all due to the idea that sys.stdin or sys.stdout might get replaced with streams that don't have a binary buffer. The simple solution is just not to do that (and it's pretty easy; instead of replacing with a StringIO?, replace it with a wrapped binary buffer). Then the code is quite simple

    import sys
    import shutil
    for filename in sys.argv[1:]:
        if filename != '-':
            try:
                f = open(filename, 'rb')
            except IOError as err:
                msg = 'cat.py: {}: {}\n'.format(filename, err)
                sys.stderr.buffer.write(msg.encode('utf8', 'surrogateescape'))
                continue
        else:
            f = sys.stdin.buffer
        with f:
            shutil.copyfileobj(f, sys.stdout.buffer)

Python's surrogateescape'd strings aren't the best solution, but I personally believe that treating unicode output streams as binary ones is even worse.

reply

Jweb_Guru 8 hours ago

There is no ~str, but when there was it was largely identical to what String is now.

reply

--

didip 8 hours ago

Question for Rust peeps out there:

Is there a documentation on how to build single binary executable for Rust?

reply

steveklabnik 8 hours ago

I'm assuming you're referring to static linking.

Right now, we depend on glibc, and it cannot be statically linked. By default, Rust applications statically link everything but glibc already.

At least one core team member is strongly advocating for musl support for truly standalone binaries in the 1.1 timeframe, but nothing is likely to change for 1.0, and it's too early to decide on 1.1 features yet :)

reply

xyzzy_plugh 8 hours ago

  > Right now, we depend on glibc, and it cannot be statically linked.

Could you expand on this? Why can it not be statically linked?

reply

spystath 8 hours ago

Name resolution libraries are loaded dynamically [1,2], so glibc can't be linked statically if you want NSS to work. I gather there might be a way to explicitly link to a specific resolution library but this is practically unsupported/poorly documented.

[1]: https://sourceware.org/glibc/wiki/FAQ#Even_statically_linked...

[2]: http://stackoverflow.com/questions/3430400/linux-static-link...

reply

xyzzy_plugh 8 hours ago

I understand this limitation. I can currently get around this by using --enable-static-nss.

Does rust currently expose any way to do this? Could I perform linking myself as a workaround?

reply

steveklabnik 8 hours ago

> Could I perform linking myself as a workaround?

You can but it requires lots of black magic, and certainly a nightly rather than beta Rust ;)

http://mainisusuallyafunction.blogspot.com/2015/01/151-byte-... is an old blog post that can get you started.

reply

--

cjcole 13 days ago

I've written about 10 thousand lines of Rust at this point and hope to write much more. After "suspending frustration" and internalizing the borrow checker and the idioms it just works for me now.

That said, I still get minor jets of frustration when this works:

let y = a.b.c;

But this does not:

let x = a.b; let y = x.c;

And this works:

let x = foo.bar(); let y = foo.baz(x);

But this does not:

let y = foo.baz(foo.bar());

due to scoping and borrow checker rules. Papercuts, yes, but annoying nonetheless.

That said, Rust (effortlessly) caught what could have been a very dangerous bug for me last night (I'm using a Rust wrapper around Lua): when you call "tostring" in Lua it returns you a reference to a string on the Lua stack. If you then pop the Lua stack the reference becomes invalid. If you then use the reference to the string, well... (This is akin to iterator invalidation.)

Rust's borrow checker flagged the error and its error reporting made the problem obvious. This will make up for a very large number of papercuts.

reply

cjcole 13 days ago

The core problem is that the set of safe programs is considerably larger than the set of programs which pass a tractable, maintainable, and (potentially) provably safe compiler (including borrow and type checkers).

You want to enlarge the latter set, since that lessens programmer frustration and increases expressiveness, but in the context of a non-garbage-collected language that often requires adding complexity to the type and borrow checkers which may in turn end up introducing bugs.

A discussion of various run-ins with the borrow checker and some details of attempts to reduce the friction:

https://github.com/rust-lang/rust/issues/6393

'Both @pcwalton and @zwarich spent some time trying to actually implement this work (with a possible RFC coming hand-in-hand). They ran into some unexpected complexity that means it would take much more work than hoped. I think everyone agrees with you that these limitations are important and can impact the first impression of the language, but it's hard to balance that against backwards-incompatible changes that are already scheduled.'

[added on edit:]

In particular, here is a very nice statement of the issue(s):

https://github.com/rust-lang/rust/issues/6393#issuecomment-2...

nikomatsakis: 'I'd also like to have more progress on a soundness proof before we go about extending the system.'

And here again is the tension between complexity in the compiler and the coverage of the space of safe programs.

reply

--

Animats 14 days ago

Often, it's not at all obvious what the type inference system is doing. The borrow checker has better error messages than the type checker.

As for cyclic data structures, one would often like to have a primitive which encapsulates a forward owning pointer and a backpointer tied to it, with the backpointer unable to outlive the forward pointer. That's enough to handle most tree-like structures, and doesn't require reference counts or reference-counted weak pointers. It's a compile-time concept checkable by the borrow checker. Something to think about for Rust 2.

--

Jweb_Guru 14 days ago

> As for cyclic data structures, one would often like to have a primitive which encapsulates a forward owning pointer and a backpointer tied to it, with the backpointer unable to outlive the forward pointer.

One can already do this with `&T` (for the backpointer) and `Box<T>` or `&mut T` (for the forward pointer). The hard part is letting you safely mutate something once the back reference has been added, without any runtime checks and with arbitrary data in the pointer. I don't believe what you've described addresses that issue, but maybe I'm missing something.


Animats 14 days ago

My point is that it's possible to extend the Rust model to check this at compile time. The borrow checker has to understand backpointers. The owned object cannot outlive the owner, so a simple backpointer is safe in that sense. But ownership can change, and when it does, the backpointer must also change.

The Rust ownership model can almost do this now. A move-type assignment to the forward pointer which causes a change in ownership would have to do a mutable borrow on the backpointer to get exclusive use, then change the backpointer to the new owner. That prevents a change to the backpointer while it's being used for something. The borrow checker would have to understand that those two pointers are tied together by an invariant. There would need to be syntax for back references, understood by initialization as well as moves.

It's such a common case that it's worth the special handling. It would let you do trees and doubly-linked lists without reference counts.

The Rust ownership model is very powerful. I was fooling around with something like this a decade ago as an improvement to C++, but all I expected to get out of it was the elimination of reference count updates when you passed an object to a function without giving the function permission to delete or keep the object. Rust pushes that model much harder, and it works. It can be pushed even further to make more common use cases compile-time checkable. This would cover more common idioms from C++.

Another possible extension is lifetimes on objects allocated with "new". If you need to add something to a tree, you want the new objects to have the same lifetime as the tree. If you could specify a lifetime in "new", resulting in an object being allocated in an outer scope, that would cover the idiom where you call some "add to data structure" function and it creates objects to be added to the data structure. Currently in Rust, you have to create new objects from within the scope that owns the data structure, then call some function to install them in the data structure.

The idea of creating an object in an outer scope seems strange, but when you create a closure, you're doing just that.


tomjakubowski 13 days ago

> One can already do this with `&T` (for the backpointer) and `Box<T>` or `&mut T` (for the forward pointer). The hard part is letting you safely mutate something once the back reference has been added, without any runtime checks and with arbitrary data in the pointer.

Can one? A &mut T and &T both pointing to the same memory and existing at the same time causes undefined behavior, and such a pairing could only be constructed via a compiler bug or (wrong) unsafe code.

reply

--

http://www.evanmiller.org/a-taste-of-rust.html

https://news.ycombinator.com/item?id=9546382

--

shadowmint 3 days ago

    Whenever I work in Rust, I find myself having a good time mucking around 
    with the abstractions, but not really getting anything done toward the 
    problem I’m trying to solve.

I hear this a lot.

There's a lot of good stuff in rust, but people seem to get massively caught up in making the borrow checker happy and lose sight of the actual task.

Maybe the idea of a drop in GC type for rust (ie. https://github.com/Manishearth/rust-gc/) has some merit after all...

reply

eddyb 3 days ago

A safe GC would be immutable by default, so you end up with a lot of Cells (or worse, RefCells?) to emulate a mutable GC like JS or Go.

reply

Manishearth 3 days ago

Pretty easy to do `type MyGc?<T> = Gc<RefCell?<T>>`.

Even better, put trait objects everywhere. Now we're Java. Go sit in a corner and think about what you've done :P

When designing rust-gc with mystor at one point I joked about writing a syntax extension that converts everything in a crate to a GCd, RefCelld?, trait object, so that Java programmers have no problem converting.

reply

---

justthistime_ 4 days ago

> Rust itself isn't particularly influenced by Scala

Agree! Sadly, they didn't use all the lessons Scala could offer them when designing their own language. :-(

Painful to watch as they introduce stuff where Scala developers already know that it's completely broken.

reply

steveklabnik 4 days ago

Would you mind expanding on what these things are?

reply

lmm 4 days ago

The cascade of efforts at error handling, which I think at one point involved a macro and a magic method name, rather than getting on with higher-kinded types to allow them to do it right.

Allowing "return".

Mandating braces for functions and control flow constructs.

Rust iteration, which is both more limited and less safe (laziness) than the scala/ruby style of passing a closure.

reply

kibwen 4 days ago

  > The cascade of efforts at error handling

You must be referring to ancient Rust with its condition system and such. Modern Rust code just uses Result types.

  > rather than getting on with higher-kinded types to allow 
  > them to do it right

Er, no, you don't need HKTs to "do it right", you'd just need HKTs to do it generically. Rust's error handling works fine for specific types; you can even write macros to emulate do-notation (and people have).

  > Allowing "return"

Welcome to the realities of imperative programming. :) Unlike Scala, Rust does not aspire to functional godhood.

  > Mandating braces for functions and control flow constructs

Scope in Rust is very, very important for expressing when references go out of scope and when resources are freed. Leaving scope implicit would be massively frustrating.

  > the scala/ruby style of passing a closure

Rust had internal iterators for years, and the entire `for` construct was based around them. They were removed because they were found wanting.

As I've mentioned in a sibling comment, the lessons that Scala may have learned in its life do not necessarily apply to Rust. They are very, very different languages.

reply

steveklabnik 4 days ago

Thank you! It's really hard to understand where criticism is from without specifics, even if you're not the OP.

I would love higher kinded types, but most of our users are asking for other type system features first. What we've done doesn't preclude a HKT style in the future, as the signatures are the same.

I'm not aware of an imperative language that doesn't allow early returns, maybe there are some, but I find 'guard clauses' to significantly combat rightward drift.

Braces are to appeal to our core audience, who have been using braces and semicolons for decades. Absolutely, 100% subjective though, I know people who prefer the whitespace-based syntax, but you really only get one or the other.

  > Rust iteration, which is both more limited and less safe (laziness) than the scala/ruby style of passing a closure. 

I'm not sure specifically what you mean here, around both safety and closures.

Here's Rust:

    let v = vec![1, 2, 3];
    let v1 = v.iter().map(|x| x + 1).collect()

Here's Ruby:

    v = [1, 2, 3]
    v1= v.map {|x| x + 1 }

Other than the laziness, both are "pass a closure." And I'm not sure how safety ties in here at all.

Most of these things seem like subjective preferences, rather than things that are "completely broken."

reply

lmm 4 days ago

> I would love higher kinded types, but most of our users are asking for other type system features first. What we've done doesn't preclude a HKT style in the future, as the signatures are the same.

I think many users don't know that they want higher-kinded types, only that they want particular features.

> I'm not aware of an imperative language that doesn't allow early returns, maybe there are some, but I find 'guard clauses' to significantly combat rightward drift.

I don't know what you consider an "imperative language"; I'd consider Scala an example. I think a decent error mechanism (which Rust does have, even if the implementation is ad-hoc and reliant on a macro) provides a better way to express guard clauses. I think return is untenable in a language with lambdas, because any possible choice of semantics for return inside a lambda (including "compile error") will confuse a decent proportion of programmers.

> Braces are to appeal to our core audience, who have been using braces and semicolons for decades.

If by your core audience you mean C++ users then they're used to being able to omit braces on if/while/etc.

> Other than the laziness, both are "pass a closure."

I'm objecting to the .iter() part. Or perhaps more generally to the lack of something as extensible as do or for/yield. The for/yield construct turned out to be far more valuable to Scala than was realised initially.

> And I'm not sure how safety ties in here at all.

I meant unsafe in the colloquial sense. I think laziness makes programs very hard to reason about.

> Most of these things seem like subjective preferences, rather than things that are "completely broken."

I agree that they're not completely broken, but I think they're more than subjective; in many cases Scala has tried the various options over the years. We've tried exceptions and macros and found a better way of doing error handling. We've had return in the language and discovered it to be more trouble than it's worth. We've had a notoriously flexible syntax around function calls and seen a consensus on the best style develop over the years. We've seen some control-flow structures virtually wither away and others become indispensable.

reply

pcwalton 4 days ago

> Or perhaps more generally to the lack of something as extensible as do or for/yield. The for/yield construct turned out to be far more valuable to Scala than was realised initially.

Yield doesn't make a whole lot of sense in a systems language with machine-level control over stacks and allocation. It doesn't make a whole lot of sense in a systems language where the idea of jumping out of a stack frame either means doing strange low-level stack jumping tricks (defeating standard CPU/compiler optimizations) or allocating behind your back (which is something that Rust never does). Very early Rust actually tried using yield for iteration and it didn't work.

> We've tried exceptions and macros and found a better way of doing error handling. We've had return in the language and discovered it to be more trouble than it's worth.

We've experimented with these too. And I use return all the time in Rust. It's really useful and I would hate to see it go away.

I think Rust and Scala are just different languages. One's an applications language built on the JVM and the other is a low-level systems language with manual memory management. That difference means that different decisions may be appropriate.

reply

---

nbaksalyar 4 days ago

Rust doesn't require a VM or any prerequisites installed on a target system. In this regard it's comparable to Go (though I'm not sure that it's viable to statically compile everything into one binary, but technically it should be possible). Anyway, it compiles down to the machine code, so at most you'll need to have a bunch of libraries along with your executable file.

Hopefully you'll have a lot of fun with learning! :) Oh, and almost forgot: Rust community is one of the best that I've seen, so feel free to ask any questions on http://users.rust-lang.org - I'm sure there's a lot of people who'll be glad to help you.

reply

steveklabnik 4 days ago

Small detail: most Rust programs do need glibc installed. Unless you've built with MUSL specifically.

reply

---