Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
Bognar
Aug 4, 2011

I am the queen of France
Hot Rope Guy
Rust released the alpha version of 1.0 recently, meaning there's finally a (mostly) stable standard library and feature set, so I figured it's about time we had a thread about it.

What is Rust?
Rust is a statically typed, compiled, systems programming language with a significant amount of safety built into the compiler. The goal of Rust is to be used in situations similar to where you would use C or C++, but provide features in the language to keep you safe from the hard parts of systems programming like leaking memory, race conditions, dangling pointers, and other. Rust has a heavy emphasis on zero cost abstractions in the language to provide all that safety. The compiler enforces these safety guarantees so that you can be reasonably sure that if your code compiles, then your code is correct and safe. Since this safety is enforced at compile time your program can run just as fast as the C or C++ program, hence the zero (runtime) cost abstractions.

While Rust is a systems language and stays mostly true to the C abstract machine model, it also has a lot of influence from functional languages. Here is an example of some idiomatic Rust to some all even numbers from 1 to 1000:

Rust code:
fn main() {
    let sum = (1..1000)
        .filter(|&x| x % 2 == 0)
        .fold(0, |a, b| a + b);
    println!("{}", sum)
}

// 249500
The filter and fold methods are in the standard library, and they accept lambdas which are written starting with the |var| syntax.

What makes Rust so safe?
The most defining feature in Rust is its ownership model. In Rust, each resource (stack memory, heap memory, file handle, struct, etc.) can only have one owner and this ownership is enforced by the compiler. A resource is automatically freed anytime its owner goes out of scope. The ownership model enforces that only one thing can make changes to a resource at any given time. If you require multiple objects to have ownership of a single resource, that can be introduced as necessary with per variable reference counting.

An owner of a resource can control the resource's deallocation, lend the resource out to borrowers, or move the ownership to a new owner. When lending a resource out (also known as borrowing), a resource can be borrowed immutably by any number of borrowers or borrowed mutably by one borrower. If a resource is borrowed immutably, no modifications can be made to that resource (by the borrowers or the owner). If a resource is borrowed mutably, only the borrower may modify the resource. All of this comes together to enforce memory safety without additional runtime baggage. This does, however, have compile time baggage and significant learning curve baggage as you struggle to satisfy the borrow checker.

Rust has runtime bounds-checking of Vector and array access, so no more straying off into random memory.

Rust strongly discourages unmanaged pointers by requiring the unsafe{} keyword around all usages. Safe code in Rust by default has no null values. Nullability can be added back in if required with the Option type that is so commonly used in functional languages:

Rust code:
fn print_value(value: Option<isize>) {
    match value {
        Some(val) => println!("The value is {}" , val),
        None => println!("That wasn't a number"),
    }
}

fn main() {
    let five = "5".parse(); // option type
    print_value(five);
    let garbage = "garbage".parse(); // option type
    print_value(garbage);
}

// The value is 5
// That wasn't a number
Note above, we also have the match keyword which requires exhaustive pattern matching. Every case must be checked (or a default provided) or the compiler will yell at you. Rust pattern matching is also used for one of my favorite FizzBuzz implementations:

Rust code:
fn main() {
    for i in 1..101 {
        match (i % 3, i % 5) {
            (0, 0) => println!("FizzBuzz"),
            (0, _) => println!("Fizz"),
            (_, 0) => println!("Buzz"),
            (_, _) => println!("{}", i),
        }
    }
}
Why should I care?
Because you're a bored and interested nerd like me. Because you've had your fair share of memory leaks and segfaults using unsafe languages. Because people on Hacker News keep talking about it and you want to offer your lovely opinion to the lovely discussion.

Where can I learn more about Rust?

Official Website: http://www.rust-lang.org/
Official Guidebook: http://doc.rust-lang.org/1.0.0-alpha/book/
Language Reference: http://doc.rust-lang.org/1.0.0-alpha/reference.html
Rust By Example: http://rustbyexample.com/

Adbot
ADBOT LOVES YOU

Bognar
Aug 4, 2011

I am the queen of France
Hot Rope Guy
Oh gently caress me I'm bad at making threads. poo poo post ahoy.

Linear Zoetrope
Nov 28, 2011

A hero must cook
I believe you'll find the superior title is "I left my compiler in the rain and got Rust".

E: But seriously, I've played with it and it's great once you get used to the memory semantics. The community is also surprisingly nice and helpful. I'm not sure how widespread adoption will be, but I hope it becomes a big language.

It also isn't afraid to crib all the best features from other languages. Haskell-like type systems, Go's channels, and so on.

Linear Zoetrope fucked around with this message at 08:13 on Jan 15, 2015

Space Whale
Nov 6, 2014

Jsor posted:

It also isn't afraid to crib all the best features from other languages. Haskell-like type systems, Go's channels, and so on.

Isn't this (in a bad way) what made C++ go wrong?

Bognar
Aug 4, 2011

I am the queen of France
Hot Rope Guy
I don't think the new features of C++ are hampering it as much as the legacy cruft that is continuing to be supported along with it.

ExcessBLarg!
Sep 1, 2001
More specifically that the features added to C++ are (have to be) done in a way that's backwards compatible to not only it's own ~30 year history, but also a 40 year old predecessor language. Needless to say, the syntax of the new features is terrible.

Workaday Wizard
Oct 23, 2009

by Pragmatica
I toyed with Rust when it was 0.6~ and I am looking forward to trying it again.

Is there a recommended autocompletion tool?

Deus Rex
Mar 5, 2005

Shinku ABOOKEN posted:

I toyed with Rust when it was 0.6~ and I am looking forward to trying it again.

Is there a recommended autocompletion tool?

If you use vim or emacs, there's racer.

space kobold
Oct 3, 2009


I've been waiting for a thread on Rust to pop up, as I'm far to lazy to have made my own.

I've got a couple of fun little projects going in Rust, the largest of which is slowly shaping into something of a game (that only consists of a terrible animation system/scene manager/ai with behavior trees as of yet). Super thrilled that it's finally hit its big 1.0 (alpha)! Though most of the standard library is marked as unstable and likes to stare threateningly at my codebase with maniacal intent to break any and all of my builds at a moments notice.

Certainly liking the language so far, as someone who primarily codes in C and Lua for fun, C# professionally, and tends to avoid C++ like the plague.

All of the big Rust irc chatrooms on irc.mozilla.org are all super active as well, with people that love to chip in and help with things. So I'd highly recommend parking yourself in one or more if you want to seriously delve into Rust.

VikingofRock
Aug 24, 2008




So I'll ask this thread's first stupid question. How does ownership work with arrays, vectors, etc? If I add a Foo to an array or vector, does that array gain ownership over the Foo? Hopefully it's clear what I'm asking here, if not I can try to whip up some code to clarify.

Linear Zoetrope
Nov 28, 2011

A hero must cook

VikingofRock posted:

So I'll ask this thread's first stupid question. How does ownership work with arrays, vectors, etc? If I add a Foo to an array or vector, does that array gain ownership over the Foo? Hopefully it's clear what I'm asking here, if not I can try to whip up some code to clarify.

Looks like it moves unless you implement copy: http://is.gd/VR7qVk

E: Note that it's considered borrowed if you box it too.

Linear Zoetrope fucked around with this message at 08:57 on Jan 29, 2015

VikingofRock
Aug 24, 2008




Jsor posted:

Looks like it moves unless you implement copy: http://is.gd/VR7qVk

E: Note that it's considered borrowed if you box it too.

Thanks, that makes sense. Now that I think about it I think that has to be the case--otherwise stuff like this wouldn't work:

code:
struct Foo{
    pub a : u32
}

fn main (){
    let foos = [Foo{a: 1}, Foo{a: 2}, Foo{a: 3}];
    for foo in foos.iter(){
        println!("{}", foo.a);
    }
}
because the Foos would be deleted at the end of the declaration if they didn't transfer ownership. I think. I'm still trying to grok Rust's ownership/lifetime model.

Linear Zoetrope
Nov 28, 2011

A hero must cook
Me too. I spent forever yesterday struggling with cryptic lifetime errors until I learned that new methods should return by value and not by pointer. It seems to be a Rust pattern to allow the code requesting the object to dictate its mutability/reference status.

space kobold
Oct 3, 2009


Once you get over the initial hurdle of everything bitching at you about lifetimes and learning the ~Rustic~ approach to things, like always returning just a plain bit of value and letting the caller decide if they want to box it (unique pointer to heap allocated memory), stick it on the stack, atomic reference count it, etc, things really start to click and you can appreciate the beauty of what they're trying to do with the language.

Then you get to things like structures holding references to data or non object-like trait objects, and get to start the whole process over again while idly scratching at your head.

The documentation is already loads better, and I'm fully anticipating a much wider array of user friendly tutorials, examples, and eventual books to start pouring out as more and more of the rust standard library gets that wonderful Stable stamp of feature lockin.

Of course they just completely broke most libraries by renaming the core io module to old_io in anticipation of redoing the whole drat thing, so we still have awhile to go yet before we get our true 1.0 release.

VikingofRock
Aug 24, 2008




space kobold posted:

Once you get over the initial hurdle of everything bitching at you about lifetimes and learning the ~Rustic~ approach to things, like always returning just a plain bit of value and letting the caller decide if they want to box it (unique pointer to heap allocated memory), stick it on the stack, atomic reference count it, etc, things really start to click and you can appreciate the beauty of what they're trying to do with the language.

Is there a guide somewhere to doing things the ~Rustic~ way? I've made my way through the Guide and Rust By Example but I haven't really absorbed stuff like what kind of value to return.

Linear Zoetrope
Nov 28, 2011

A hero must cook

space kobold posted:

Once you get over the initial hurdle of everything bitching at you about lifetimes and learning the ~Rustic~ approach to things, like always returning just a plain bit of value and letting the caller decide if they want to box it (unique pointer to heap allocated memory), stick it on the stack, atomic reference count it, etc, things really start to click and you can appreciate the beauty of what they're trying to do with the language.

I have to admit, the thing that confused me about it was largely that once things are immutable they can't really become mutable, and I'm not really clear why it suddenly works when you wrap it in a new function. I have an example to explain my confusion: http://is.gd/l5A2hj

I can do

code:
let f = &mut Foo::new();
And it works just fine, despite the fact that the body of new is

code:
let f = Foo{x: 15};
f
(I did the let statement to make sure things were as similar as possible).

However, this gives a compiler error:

code:
let y = Foo{x: 15};
let z = &mut y;
I have trouble reconciling why these two are different because they both go from ostensibly immutable variable -> mutable variable. I notice that the error says "error: cannot borrow immutable local variable `y` as mutable" (emphasis mine); which probably explains it, but it was pretty unintuitive to me. Though I guess it explains why things like borrow_mut functions work.

Linear Zoetrope fucked around with this message at 20:42 on Jan 30, 2015

Munkeymon
Aug 14, 2003

Motherfucker's got an
armor-piercing crowbar! Rigoddamndicu𝜆ous.



Dang, Rust looks way nicer than I remember it looking but

Bognar posted:

When lending a resource out (also known as borrowing)

People around here* like to misuse the word 'borrow' in normal speech like 'hey, borrow me that' and it's nails-on-the-chalkboard inside my head in the same way 'I downloaded that to you' is and that reminded me of that misuse for some reason.

*E: meaning the place where I live, not SA

Munkeymon fucked around with this message at 23:40 on Jan 30, 2015

Workaday Wizard
Oct 23, 2009

by Pragmatica
Thanks whoever fixed the thread tag :)

Linear Zoetrope
Nov 28, 2011

A hero must cook

Munkeymon posted:

Dang, Rust looks way nicer than I remember it looking but


People around here like to misuse the word 'borrow' in normal speech like 'hey, borrow me that' and it's nails-on-the-chalkboard inside my head in the same way 'I downloaded that to you' is and that reminded me of that misuse for some reason.

From what I've seen, the compiler uses "borrow" properly. All the errors say things like "x has been borrowed by" or "but x was borrowed <here>".

PleasantDilemma
Dec 5, 2006

The Last Hope for Peace
When Rust was first getting attention I remember a lot of discussion about it having built in support for concurrency like the Go language. Recently when I hear about the language everyone talks only about the memory safety aspect of the language. Does Rust still have built in support for concurrency, or am I not remembering things correctly?

sarehu
Apr 20, 2007

(call/cc call/cc)

PlesantDilemma posted:

When Rust was first getting attention I remember a lot of discussion about it having built in support for concurrency like the Go language. Recently when I hear about the language everyone talks only about the memory safety aspect of the language. Does Rust still have built in support for concurrency, or am I not remembering things correctly?

They dropped that cooperative green threads system so that it's only merely a library, instead of being something inseparable from the language (or more substantially, inseparable from the rest of the standard library).

There's still aspects of the language that pay homage to concurrency, though, like how you can only have one mutable borrow at a time (if I'm not mistaken).

sarehu fucked around with this message at 02:36 on Jan 31, 2015

Deus Rex
Mar 5, 2005

sarehu posted:

There's still aspects of the language that pay homage to concurrency, though, like how you can only have one mutable borrow at a time (if I'm not mistaken).

This isn't really an homage to concurrency, it's required to safely implement things like a growable vector. If you could mutably borrow a Vec twice (or take out a mutable borrow while also borrowing it immutably), you could end up with a pointer to memory no longer owned by the Vec (like in the classic C++ case of appending to a std::vector while iterating over it).

The language does still have some built-in nods to concurrency with things like the Send and Sync traits, which respectively mark types as being safe to send from one thread to another and mark types which can safely be aliased immutably from multiple threads.

sarehu
Apr 20, 2007

(call/cc call/cc)
Oh. For some reason I was only thinking mutable borrows on multiple threads would be a bad idea because I did not consider how coarse-grained borrowing would affect other things. If only they had typestate so that you could express borrowing more precisely...

Bognar
Aug 4, 2011

I am the queen of France
Hot Rope Guy
Here's a neat little project where they've built a Minecraft world renderer in Rust:

https://github.com/PistonDevelopers/hematite

space kobold
Oct 3, 2009


I'm currently working on building an Entity Component System for a (potential) game I'm making. Only just started a few days ago, but it's slowly shaping up.

Current usage looks like:
Rust code:
let mut em = EntityManager::new();
let ent = em.create_entity();

em.add_component::<component::Spatial>(ent);
em.add_component::<component::Age>(ent);
// adding the Age component should cause the entity to be registered with the Aging system.
assert!(em.is_managed::<system::Aging>(ent));

// this will age the entity by one second.
em.update(1.0);

let age_component = em.get_component::<component::Age>(ent).unwrap();
// age_component.age should now be 1.0
assert!(age_component.age > 0.9);
assert!(age_component.age < 1.1);

let position = em.get_mut_component::<component::Spatial>(ent).unwrap();
position.x = 10.0;
position.y = -300.0;
Only used four unsafe blocks and some pretty hacky looking macros! :eng99:

edit: link removed, no longer valid.

space kobold fucked around with this message at 20:43 on Mar 9, 2015

Linear Zoetrope
Nov 28, 2011

A hero must cook
I tried to make an ECS too. How did you make the list of components? I couldn't figure out how to make a separate list for each component generically.

space kobold
Oct 3, 2009


I made a huge macro that builds a static list of components, using some unsafe blocks to manipulate the signature for each components array of data.

Bit hacky, but it seems to work. The list is constructed like so:
code:
use std::default::Default;

#[macro_use]
mod macros;

/// trait shared by all components
pub trait Component: 'static+Default {}

/// component representing coordinates in 2d space.
#[derive(Copy, Debug, Default, PartialEq)]
pub struct Spatial {
    pub x: f64,
    pub y: f64,
}
impl Component for Spatial { }

build_component_list!(
    Spatial,
);
edit: link removed, no longer valid.

space kobold fucked around with this message at 20:43 on Mar 9, 2015

Linear Zoetrope
Nov 28, 2011

A hero must cook
Yeah, a macro was my intuition, but I haven't ventured into macro land yet. Looking at yours will probably be educational.

space kobold
Oct 3, 2009


blog.rust-lang.org posted:

Rust 1.0: status report and final timeline

Feb 13, 2015 • The Rust Core Team

It’s been five weeks since we released Rust 1.0-alpha! Before this release cycle finishes next week, we want to give a status report and update on the road to 1.0 final.

TL;DR: Rust 1.0 final will be released by May 15, 2015

...

http://blog.rust-lang.org/2015/02/13/Final-1.0-timeline.html

We've got a release date folks!

Linear Zoetrope
Nov 28, 2011

A hero must cook
Y'know, I think the worst thing about Rust is when the borrow checker can't prove something obviously safe is safe. If you have a function like fn get_something(&mut self) -> &mut MyType, and you match on self in the function, you absolutely cannot use any `ref`s on a pattern where you want to return self or Rust will refuse to acknowledge the borrow ends when the match is evaluated and the function returns.

Deus Rex
Mar 5, 2005

Jsor posted:

Y'know, I think the worst thing about Rust is when the borrow checker can't prove something obviously safe is safe. If you have a function like fn get_something(&mut self) -> &mut MyType, and you match on self in the function, you absolutely cannot use any `ref`s on a pattern where you want to return self or Rust will refuse to acknowledge the borrow ends when the match is evaluated and the function returns.

I don't think I understand your description of this particular problematic case, but in general yeah -- it can be very annoying when a borrow is held for much longer than it needs to, and you have to resort to scope convolution to satisfy the borrow checker.

pcwalton had that huge patch to add "SEME regions" to later support non-lexical borrow scopes but it never landed, and I doubt anything like that will land before 1.0 :-\

Deus Rex fucked around with this message at 19:36 on Feb 27, 2015

Linear Zoetrope
Nov 28, 2011

A hero must cook
Here's an example of what I'm talking about. Basically pattern matching on mutable things can be bad:

http://is.gd/l61Zav

code:
use std::cmp::Eq;

#[derive(Debug)]
enum List<T: Eq> {
    Cons {
        val: T,
        next: Box<List<T>>,
    },
    Nil,
}

impl <T: Eq> List<T> {
    fn find_mut(&mut self, query: T) -> &mut List<T> {
        use List::{Cons, Nil};
        match *self {
            Nil => self,
            Cons{ref val, ref mut next} =>
            if *val == query { self } else { next.find_mut(query) },
        }
    }
}

sarehu
Apr 20, 2007

(call/cc call/cc)
Looking at some linked list code, such as http://doc.rust-lang.org/0.12.0/src...t.rs.html#37-41 or some other linked list I saw, I notice that they wrap the unsafe pointers in a Rawlink<T> type. What is the purpose of doing this? Right now I assume it's so they only have to say "unsafe" in one place, or some bogus pretense at safety by making the value accessible only via an Option type as if null pointer dereferences were the danger and not use-after-free. Am I wrong?

fart simpson
Jul 2, 2005

DEATH TO AMERICA
:xickos:

So the beta is out. I've been keeping my eye on Rust for a while and downloaded it. It seems pretty cool so far!

space kobold
Oct 3, 2009


The best part about it is that everything in it is more or less feature locked and fairly stable, so every single library in existence isn't going to break every other week so long as they're building against the beta.

Linear Zoetrope
Nov 28, 2011

A hero must cook

space kobold posted:

The best part about it is that everything in it is more or less feature locked and fairly stable, so every single library in existence isn't going to break every other week so long as they're building against the beta.

I did run into a bunch of feature gates I had to switch back to nightly for, though. Some of the stuff in collections is really, really useful. I can live without box syntax, but the collections feature has some really good stuff in it.

What's the general stance on using generic parameterization purely for static side effects? That is, parameterizing a function with type T to alter its behavior. I had several algorithms (specifically, a multi-armed bandit solver) and I decided to make a function like:

code:
fn bandit_solver<B,T>(bandit: B) -> (B::Arm,f64) 
                             where B: Bandit, T: BanditSolver<B>
Because the solver, though stateful, wasn't meaningfully any sort of object, and made no sense for its resources or lexical scope to be outside the function. So I decided to parameterize the function with the algorithm. It actually worked pretty cleanly, but I'm not sure how I feel about it. The syntax was definitely a bit ugly with all the angle brackets within angle brackets.

Linear Zoetrope fucked around with this message at 07:55 on May 5, 2015

sarehu
Apr 20, 2007

(call/cc call/cc)
A reason to do that is to avoid making virtual function calls. If the behavior of the solver was not entirely defined by the type, you could pass in the solver as a function parameter of type T (or reference to T or something) and still get that advantage.

Reasons not to do that: compile time, executable size, performance costs of code bloat.

Edit: If virtual function call performance is not a concern, I'd go with the version that uses virtual function calls. Either way, at the call site you're going to be parameterizing the behavior of the function, either as a template parameter or a runtime parameter. It is perfectly reasonable for a thing parameterizing the behavior of a function to be an "object".

sarehu fucked around with this message at 02:00 on May 5, 2015

space kobold
Oct 3, 2009


I've got one dependency that only compiles on the 1.1 nightly, and another dependency that only compiles on the 1.0 beta. Welp.

:negative:

So much for my earlier prediction of things being much more stable and uniform as we approach that big 1.0 release.

fart simpson
Jul 2, 2005

DEATH TO AMERICA
:xickos:

space kobold posted:

I've got one dependency that only compiles on the 1.1 nightly, and another dependency that only compiles on the 1.0 beta. Welp.

:negative:

So much for my earlier prediction of things being much more stable and uniform as we approach that big 1.0 release.

Obvious solution is to compile half your program as a cli executable and call it from the other half

Adbot
ADBOT LOVES YOU

Deus Rex
Mar 5, 2005

space kobold posted:

I've got one dependency that only compiles on the 1.1 nightly, and another dependency that only compiles on the 1.0 beta. Welp.

:negative:

So much for my earlier prediction of things being much more stable and uniform as we approach that big 1.0 release.

Guhh? What's the one that compiles on the beta but not the 1.1 nightly? That sounds like a regression that should be reported...

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply