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
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

Adbot
ADBOT LOVES YOU

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

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.

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

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>".

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.

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.

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.

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) },
        }
    }
}

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

Linear Zoetrope
Nov 28, 2011

A hero must cook
Rust is a language that I think is good, but it's also a language that I think would make a horrible first one, and learning it is much better served by doing than reading. You can make that last point for all languages, but in Rust especially a lot of language quirks like lifetimes and such are really opaque until you wrestle with the compiler and spend a couple hours on Google a few times. It's hard to get a feel for them with a tutorial that simply illustrates correct and incorrect lifetime parameters.

Linear Zoetrope
Nov 28, 2011

A hero must cook
Also, I think the more "Rustic" way is to use an enumerated type instead of a faux-null-pointer option

code:
enum BSTNode {
    Node{left: Box<BSTNode>, right: Box<BSTNode>, value: i64},
    Sentinel,
}
So instead of having the option to have a left or right pointer, you always point to a node which is either a full node or an end marker. So an empty tree is simply a single Sentinel.

Linear Zoetrope
Nov 28, 2011

A hero must cook
Can somebody please help me figure out how to do a tree traversal in a loop rather than recursively? The recursive version is blowing up my stack.

The recursive code is essentially:

code:
fn traversal(&mut self, world &mut T) -> Reward {
    if world.is_terminal() {
        return 0.0;
    }

    let action = self.choose_action(world);
    let reward,state = world.act(action);
    
    // Find a child matching that state, or generate a new child
    // this is returned as an &mut MyNodeType
    let next = self.act(state);

    let cum_reward = reward + next.traversal(world);

    self.total_reward[action] = self.total_reward[action] + cum_reward;

    cum_reward
}
This is wrapped in something like:

code:
fn search(world: &mut T, num_trajectories: usize) -> Action {
    let mut root = MyNodeType::new();

    for _ in 0..num_trajectories {
        root.traversal(world);
        world.reset();
    }

    root.select_best_action();
}
My best attempts to loopify this have ended in failure:

code:
fn search(world: &mut T, num_trajectories: usize) -> Action {
    let mut root = MyNodeType::new();

    for _ in 0..num_trajectories {
        let tree = &mut root;
        while !world.is_terminal() {
            let action = tree.choose_action(world);
            let reward,state = world.act(action);
            
            tree = tree.act(state);

            // ... stuck now ...
        }
        world.reset();
        tree = root;
    }

    root.select_best_action();
}
When I try to reassign tree it claims that the borrow lasts until the end of the OUTER loop, so I can't rebind it. And even if I could, I have no idea how to do the post-evaluation of the reward if I could get that far. That would be easy in not-Rust due to laxer aliasing rules (parent pointers, maintain a stack of nodes, whatever), but the borrow checker hates me.

Specifically, it says the problem line is a mutable borrow at the line I call tree.act, which is where it borrows the tree's child out of the node's state->child hash map.

Any ideas? I can't do this recursively because unfortunately it overflows the stack due to the depth of the state space. (I'm running monte-carlo tree search on Space Invaders). I can depth limit it, of course, but even if I do that I'll be able to go deeper if I can unroll this.

Linear Zoetrope
Nov 28, 2011

A hero must cook
It turns out the answer was "use Rc<RefCell<blah>>". That was a fun, if somewhat frustrating, adventure. I eventually went back to the recursive code because it turns out the iterative code had almost no benefit -- the stack overflows at pretty much the same point whether I'm using a homebrewed stack or a recursive one, which I definitely didn't expect!

Linear Zoetrope
Nov 28, 2011

A hero must cook

Bongo Bill posted:

unsafe {} doesn't mean the code within is not safe; rather, it means that the memory safety of the block is promised by the programmer, instead of the compiler. The programmer might be wrong, of course. If you are trying to do something that you know is fine, but the compiler isn't smart enough to prove, that's exactly when you should be using unsafe {}. But it's probably wiser to implement it in such a way that the compiler doesn't need to trust you, wherever possible.

Hmm, that's an interesting way to look at it. The language is essentially a constructivist proof in terms of safety, in which parts of the program have two possibilities as far as the compiler is concerned: safe or unproven. When the borrow checker fails something, it's because it can't prove it's safe (rather than proving it unsafe), and the unsafe block tells it that its unprovability is okay.

I'd avoided unsafe except for FFI code, but thinking of it that way makes it a bit more palatable to use it when I'm sure something is safe.

Though I would imagine that unsafe also prevents it from passing noalias metadata to LLVM in some cases (though the code may still likely be faster than Rc<Refcell<T>>).

Linear Zoetrope
Nov 28, 2011

A hero must cook

Jo posted:

I've been dicking around with Rust 1.0 recently and started fiddling with some Project Euler exercises. One thing that has been biting me is this: at times I'd like to use a variable x as a usize and other times as u64. In this case, I have one function which builds a sieve for the sieve of aristophanes (Vec requiring a usize) and another which does a multiplication into this requiring a u64. Is there an idiomatic way in Rust to use a numeric type, or am I attacking this from entirely the wrong angle?

You can do a type conversion with (x as usize), if you're on a 64-bit machine this is probably a no-op at runtime. Though it sounds like your multiplication function should probably be generic over the Mul trait.

Unless you're multiplying by a constant. At the moment, unfortunately there's no good way to do generic arithmetic with any constants.

Linear Zoetrope
Nov 28, 2011

A hero must cook
Just throw type conversions around. IME it's pretty common, at least until associated constants become a thing. That or refactor the prime factor code to take a usize.

Linear Zoetrope
Nov 28, 2011

A hero must cook

VikingofRock posted:

Agreed. It seems like Rust tooling is really taking off, which is very exciting to me.

Rust's runtime debugging really needs some work, though. If you have an erroneous `unwrap` you helpfully get the line number of the source code for Option it calls panic! on rather than the actual part of your code. This would be fine if RUST_BACKTRACE wasn't utterly useless on non-Linux systems, on my Windows machine it helpfully prints:

code:
thread '<main>' panicked at 'panic', main.rs:2
stack backtrace:
   0:           0x43bc4b - <unknown>
   1:           0x443eae - <unknown>
   2:           0x40555c - <unknown>
   3:           0x401592 - <unknown>
   4:           0x40151f - <unknown>
   5:           0x443948 - <unknown>
   6:           0x42ea11 - <unknown>
   7:           0x44384d - <unknown>
   8:           0x4016ea - <unknown>
   9:           0x4013b4 - <unknown>
  10:           0x4014e7 - <unknown>
  11:     0x7ffa905a2d91 - <unknown>
This is just a "hello, panic!" program, basically. Panics first thing in main.

Gee, thanks, Rust, I learned so much. It's a good thing 98% of your Rust bugs the compiler catches, and 1.5% are logic errors that result in incorrect computations. Runtime panics happen so rarely this is at least tolerable. I can mostly get around it by always using expect instead of unwrap and making sure each possible panic has a unique error message.

Linear Zoetrope
Nov 28, 2011

A hero must cook
That's pretty great.

The Cavern of COBOL › Rust: error: The Cavern of COBOL does not live long enough

Linear Zoetrope
Nov 28, 2011

A hero must cook
I'm a bad Rust programmer because once some data structures, especially graphs or trees that require knowing their parents, are involved, I inevitably end up with a couple unsafe blocks. Like, I make 95% of my Rust code safe. I even endeavor to make it effectively impossible to have runtime crashes in FFI wrappers. But a lot of data structures? My feelings end up being, "God drat it, mom! Don't tell me how to live my life! I know what I'm doing!"

E: Actually, on a mostly unrelated note, can someone explain Box to me? I mean, I know what it does. It's a lot like a malloc'd pointer in C. I just can never find a place to use it because every time I try it feels like the Box type ends up infecting the return stack of the entire program.

Linear Zoetrope fucked around with this message at 00:09 on Feb 20, 2016

Linear Zoetrope
Nov 28, 2011

A hero must cook

VikingofRock posted:

Well if you really want to get fancy, you could do something like what hyper (an http library) does for Responses, and verify this at type level at compile time. Here's an example of what that might look like for this case.

edit: To be a bit more explicit (and maybe save you some reading), the idea is to make a Variable type, which is parameterized over a type that can be either Unassigned or Assigned. Then you implement an assign() function for Variable<Unassigned>, which consumes the Variable<Unassigned> and returns a Variable<Assigned>. Because of this consuming, you can guarantee that a Variable can be assigned at most once, and because there is no assign() function for Variable<Assigned>, you can guarantee that you can't re-assign a variable once it has been assigned.

Though the immediately obvious problem that jumps out at me with this scheme is it makes loops problematic if you need to assign in those.

code:
let mut x = item::<Unassigned>::new(stuff, Unassigned)

loop {
    do_stuff();
    if some_condition() {
        x = item::<Assigned>::new(stuff, Assigned(thing));
        break;
    }
}
Won't work for typing reasons.

Linear Zoetrope
Nov 28, 2011

A hero must cook
Oh yeah, you can do that. But I mentioned loops specifically because for most other constructs you can use a let rebind pattern:

code:
let x = Item::<Unassigned>::new(stuff, Unassigned)

let x = if conditional {
   x.assign(whatever)
} else {
   x.assign(something_else)
};
For instance.

I don't know if it's a good pattern, but I use it semi-frequently to create an "x prime" variable where you're deriving a value from some earlier value that's never going to be used again.

Linear Zoetrope fucked around with this message at 04:37 on Mar 22, 2016

Linear Zoetrope
Nov 28, 2011

A hero must cook
I think the Box is getting dropped as soon as svo_create exits, meaning the memory is freed. This is why it's working the first time -- there's really no guarantee how many times it will work, but you're probably getting lucky and then suddenly the memory gets corrupted and everything is terrible. You probably also need to do something like:

code:
let external_svo = [...];
let mut svo_box = Box::new(external_svo);
unsafe { 
    mem::forget(svo_box);
    mem::transmute(svo_box)
}
Then you have to remember to explicitly call a destruction function later to manually destroy the block using... uh... I'll be honest I never use forget so I'm not sure, something like:

code:
#[no_mangle]
pub extern "stdcall" fn svo_destroy(svo: *mut ExternalSVO<'static>) {
    let svo: Box<ExternalSVO<'static>> = unsafe { mem::transmute(svo) };
    drop(svo);
}
probably works.

Linear Zoetrope fucked around with this message at 20:51 on Mar 27, 2016

Linear Zoetrope
Nov 28, 2011

A hero must cook
Oh yeah, it's definitely the closure, I didn't even notice he was closing over anything, I thought he was using the &Fn as a straight up alias for extern fn.

Linear Zoetrope
Nov 28, 2011

A hero must cook

VikingofRock posted:

In my experience, working with arrays in Rust totally sucks and will likely continue to suck until they add dependent typing (which is planned, but which seems somewhat far-off). For now, I'd just use slices and save yourself the headache.

Wait, what? I thought Dependent Typing was a No Go for Rust. I mentioned it a few times in discussions on the issue tracker, and people mentioned they abandoned the idea because it wasn't a "good fit". Which is a shame because I'd really love it. Especially because first-class dependent typing isn't much work away from a full-on theorem prover which means you can prove some pretty strong guarantees for your implementations. (Unless Dependent Typing is a consequence of HKTs? I feel like people wouldn't have vehemently denied plans for dependent types if that were the case though, given that HKTs are very much in the works, though they don't think they can get it done in 2016).

Anyway, I have some questions about traits. Or rather, a few trait requirements:

1. Why does Fn require FnMut require FnOnce? Having FnOnce be the base type almost seems backwards to me, especially since this is roughly akin to Take By Immutable Reference -> Take By Mutable Reference -> Take By Move. It feels like taking by reference should be the "least onerous" to implement. But regardless it feels like all these traits should be disjoint, rather than extensions of each other. (Not that you generally implement the Fn traits yourself anyway).
2. Is there any case where you'd have Sync but not Send? It feels like it would have to be a pretty drat wonky scenario, like some of the OS X Cocoa weirdness where certain calls HAVE to be made on the main thread, but sync wouldn't fix that problem.

Linear Zoetrope fucked around with this message at 08:40 on Apr 5, 2016

Linear Zoetrope
Nov 28, 2011

A hero must cook

Arcsech posted:

On this note, if anyone has a link to a good explanation of Send & Sync besides the official docs, could you post it?

Like I get it in theory (Send means it's safe to transfer between threads, Sync means it's safe to access from multiple threads), but I haven't yet quite wrapped my head around it in practice. Like, I see the Arc<Mutex<ActualThing>> pattern a fair bit, but I'd like somewhere that does a deeper dive on an/a few examples.

My interpretation of that is that Mutex makes it Sync, and Arc makes it Send, but I'm still not confident I'd know how to use it myself (especially Arc). And I'm coming up on a point in the project I'm working on where that would be super helpful.

Mutexes are both Send and Sync. If you're doing a fork/join where your data will never go out of scope, you can just create a Mutex and send a reference to each thread. The Arc<Mutex<T>> pattern is so you can send a reference to a bunch of threads, return, and continue doing other stuff in the thread that spawned the other threads.

Linear Zoetrope
Nov 28, 2011

A hero must cook
It has something to do with your implementation of split_threshold, quads, or the definition of SubImage, because the trivial version works on the Playground:

code:
#[derive(Copy,Clone)]
struct SubImage;

impl SubImage {
    fn split_threshold(&self) -> Option<[SubImage; 2]> {
        Some([SubImage, SubImage])
    }
    fn quads(&self) -> Option<[SubImage; 4]> {
        Some([SubImage, SubImage, SubImage, SubImage])
    }

    pub fn octs(&self) -> Option<[SubImage; 8]> {
        self.quads().and_then(|quads| {
            let octs01 = quads[0].split_threshold().unwrap();
            let octs23 = quads[1].split_threshold().unwrap();
            let octs45 = quads[2].split_threshold().unwrap();
            let octs67 = quads[3].split_threshold().unwrap();
            Some([octs01[0], octs01[1], octs23[0], octs23[1], octs45[0], octs45[1], octs67[0],
                  octs67[1]])
        })
    }
}
https://play.rust-lang.org/?gist=26d3c276f2539c1f47fec1ccc77b3971&version=stable&backtrace=0

Linear Zoetrope
Nov 28, 2011

A hero must cook
Here's the problem:

code:
fn split_threshold(&self) -> Option<[SubImage;2]>;
What you want:

code:
fn split_threshold(&self) -> Option<[SubImage<'a>;2]>
BUT NOT:

code:
fn split_threshold(&'a self) -> Option<[SubImage<'a>;2]>

// or

fn split_threshold<'b>(&'b self) -> Option<[SubImage<'b>;2]>
Basically, what you wrote was filling in the <'b> version due to the lifetime elision rules. You were saying "I am asserting my borrow of self will last as long as my the sub image I return." This is impossible since quads doesn't live that long, so that borrow also can't live that long. You need to give them different lifetimes. What you really wanted to say is "whatever I return, the lifetime of the reference to the underlying data I was given needs to last at least as long, but really, how long this function's borrow of me lasts is irrelevant."

I'm probably explaining this very poorly because I don't 100% grasp the intricacies myself.

E: You probably want to make this change for the other functions too, but they don't cause this specific function to fail.

Linear Zoetrope fucked around with this message at 10:27 on Apr 8, 2016

Linear Zoetrope
Nov 28, 2011

A hero must cook

gonadic io posted:

I own a [T; 8] and would like to access the Ts out of it without copying (this is important). Then I'd like to call a FnOnce(T) -> T on each one and package them back up in an array.

I'm very confused by these requirements. Can you elaborate? For small data types, copying a pointer is likely to be no faster (and possibly slower) than copying. For large data types, it will probably have the compiler optimize it to a pointer chase rather than a full copy anyway.

Also, is it a different array you're packaging the results in or the same one? Because if it's a different one you at LEAST need to require Clone, otherwise you're just re-implementing Clone with unsafe.

Basically, I don't understand why your requirements are what they are, and why you can't use an FnMut.

I know there are some issues with being unable to move out of mutable references, so

code:
for v in array.iter_mut() {
    *v = fn(*v)
}
Won't work, but you can get around that with the `replace`, `uninitialized` trick you're using.

Linear Zoetrope fucked around with this message at 23:48 on Apr 20, 2016

Linear Zoetrope
Nov 28, 2011

A hero must cook
E: NM

Linear Zoetrope fucked around with this message at 10:38 on Jun 6, 2016

Linear Zoetrope
Nov 28, 2011

A hero must cook
I mean, there are no guarantees about where memory is freed except that it will always be freed sometime between when it's never referenced again and when it goes out of scope, the compiler is free to optimize around that AFAIK, but it will always be dropped by the time it goes out of scope (except for weird cases involving circular reference counters). I'm not sure what you're really asking about. If you absolutely need to free memory NOW and you can't use scoping to achieve it, that's what drop is for. But yes, you should assume memory is alive until you explicitly call drop or the object goes out of scope.

Also, apparently (&str).to_owned() is faster than (&str).to_string() for some reason.

Linear Zoetrope
Nov 28, 2011

A hero must cook
I don't think this is macro-able, but this is practically the example for compiler plugins in the book (they use Roman Numerals, but it's the same idea of parsing identifiers to make numbers).

Linear Zoetrope
Nov 28, 2011

A hero must cook
I actually wrote it:

code:
#![crate_type="dylib"]
#![feature(plugin_registrar, rustc_private)]

extern crate syntax;
extern crate rustc;
extern crate rustc_plugin;

use syntax::codemap::Span;
use syntax::parse::token;
use syntax::ast::TokenTree;
use syntax::ext::base::{ExtCtxt, MacResult, DummyResult, MacEager};
use syntax::ext::build::AstBuilder;  // trait for expr_usize
use rustc_plugin::Registry;

fn expand_draw(cx: &mut ExtCtxt, sp: Span, args: &[TokenTree])
        -> Box<MacResult + 'static> {

    if args.len() != 1 {
        cx.span_err(
            sp,
            &format!("argument should be a single identifier, but got {} arguments", args.len()));
        return DummyResult::any(sp);
    }

    let text = match args[0] {
        TokenTree::Token(_, token::Ident(s, _)) => s.to_string(),
        _ => {
            cx.span_err(sp, "argument should be a single identifier");
            return DummyResult::any(sp);
        }
    };

    if text.len() > 8 {
    	cx.span_err(sp, 
    		&format!("argument is interpreted as bits of a u8, a string of size {} overflows u8.", text.len()));
    	return DummyResult::any(sp);
    }

    let mut text = &*text;
    let mut total: u8 = 0;
    while !text.is_empty() {
    	// Do this at the start, it won't affect the total
    	// the first time, but if we do it after we do it
    	// once too many times
    	total >>= 1;

        if text.ends_with('X') {
        	total |= 0b1000_0000;

        } else if text.ends_with('_') {
        	// Do nothing, this is valid, but worth 0
        } else {
        	cx.span_err(sp, "argument should only contain X's and _'s, e.g. XX_X");
        	return DummyResult::any(sp);
        }
        text = &text[0..text.len()-1]
    }

    MacEager::expr(cx.expr_usize(sp, total as usize))
}

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_macro("draw", expand_draw);
}
I tried to conform to your test cases which were a bit unintuitive. It seems like you want X to denote a 1, and _ to denote a 0, and anything "missing" to automatically be 0. You start describing from the left, so

XXX = 0b1110_0000
X_ = 0b1000_0000
XXXXXX_X = 0b1111_1101

Right?

What I wrote passes all of your test cases, if I got it wrong I at least gave you a point to start from.

As a note, if you instead treat everything as starting from the rightmost bytes (that is XXXX is 0000_1111 instead of 1111_0000) you can trivially extend this to an arbitrary usize, but the way you wrote it makes that much harder.

Linear Zoetrope fucked around with this message at 01:43 on Jul 9, 2016

Linear Zoetrope
Nov 28, 2011

A hero must cook
Is the untyped Arena dead? I seem to only be able to get TypedArena on nightly.

Linear Zoetrope
Nov 28, 2011

A hero must cook
This may be foreign since you're not used to languages with pointer/reference/value distinctions like C++ or Go , but since the function takes a reference to a u32, it can't accept x since it is a u32. You need to give it a reference to a u32. You can do this with the & operator.

code:
reassign(&x);
This is what the error message:

quote:

note: expected type `&mut u32`
note: found type `u32`

means.

I'm not sure why this was working with a struct, it shouldn't unless the autoreferencing rules on structs are more lenient than I thought (usually auto-(de)referencing only happens in a few places like method calls).

Linear Zoetrope
Nov 28, 2011

A hero must cook

Asymmetrikon posted:

It worked on a struct previously because "a.b" can be implicitly dereferenced to "(*a).b" if need be - this is so operations on references to structs aren't needlessly verbose, since they're very common.

Auto-(de)referencing can get really silly if you look at its unsugaring closely, I noted in a Stack Overflow answer recently that if you have the type

code:
struct MyStruct {
    v: Vec<SomeType>,
}
And you call `self.v.iter()` it actually calls (&((*self).v)).iter()

(It's even worse than this because iter() is actually implemented on the slice intrinsic type and Vec merely implements Deref<Target=[T]>, but it wasn't important to the question)

Linear Zoetrope fucked around with this message at 02:49 on Aug 22, 2016

Linear Zoetrope
Nov 28, 2011

A hero must cook

tinaun posted:

https://github.com/rust-lang-nursery/rustup.rs/ will be the offical way to deal with this soon enough, i haven't tried it on windows yet though, but it has support for it.

Just installed it, works fine on Windows but if you have MSVC installed you may or may not want to explicitly tell it you want the gnu compiler depending on what libraries you use. It's also better if you generally use cmd/powershell/msys2 instead of just building everything from VS Code or whatever since it's fundamentally a command line application.

Linear Zoetrope
Nov 28, 2011

A hero must cook
I think Rust's terminal utilities are more targeted for MSYS2 or cmd/Powershell than Cygwin.

Linear Zoetrope
Nov 28, 2011

A hero must cook
I've been reading that book about Rust and Linked Lists, and they give the following example distinguishing

code:
enum List {
    Empty,
    Elem(i32, Box<List>),
}
and

code:
struct Node {
    el: i32,
    next: List,
}

enum List {
    Empty,
    Elem(Box<Node>),
}
They seem to claim that only the latter can use the null pointer optimization, and that if you split the first implementation in the middle you have to write a junk node, while the second implementation gets optimized as writing a null pointer in `next`.

Is this really true? It doesn't seem like the former should be immune from a null pointer optimization, though it would be more complex to infer.

Adbot
ADBOT LOVES YOU

Linear Zoetrope
Nov 28, 2011

A hero must cook
I use the VS Code plugins, which work quite well. The Sublime ones were really buggy when I used them (which wasn't that long ago), they would occasionally save hundreds of temp files in your source directory. There are Eclipse and Visual Studio ones too that support debugger functionality, but I had trouble getting the Eclipse one to work and the Visual Studio one is garbage (doesn't support Cargo, for one).

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