RUST Discussion
-
OK, I've attached a solver to my rendering engine. Shouldn't take too long to get this code into a presentable state...
01 || || 02 || || 03 || || 04 || || 05 || || _/||\__/||\__/||\_ Move from 0 --> 2 || || || 02 || || 03 || || 04 || || 05 || 01 _/||\__/||\__/||\_ Move from 0 --> 1 || || || || || || 03 || || 04 || || 05 02 01 _/||\__/||\__/||\_ Move from 2 --> 1 || || || || || || 03 || || 04 01 || 05 02 || _/||\__/||\__/||\_ Move from 0 --> 2 || || || || || || || || || 04 01 || 05 02 03 _/||\__/||\__/||\_ Move from 1 --> 0 || || || || || || 01 || || 04 || || 05 02 03 _/||\__/||\__/||\_ Move from 1 --> 2 || || || || || || 01 || || 04 || 02 05 || 03 _/||\__/||\__/||\_ Move from 0 --> 2 || || || || || || || || 01 04 || 02 05 || 03 _/||\__/||\__/||\_ Move from 0 --> 1 || || || || || || || || 01 || || 02 05 04 03 _/||\__/||\__/||\_ Move from 2 --> 1 || || || || || || || || || || 01 02 05 04 03 _/||\__/||\__/||\_ Move from 2 --> 0 || || || || || || || || || 02 01 || 05 04 03 _/||\__/||\__/||\_ Move from 1 --> 0 || || || || || || 01 || || 02 || || 05 04 03 _/||\__/||\__/||\_ Move from 2 --> 1 || || || || || || 01 || || 02 03 || 05 04 || _/||\__/||\__/||\_ Move from 0 --> 2 || || || || || || || || || 02 03 || 05 04 01 _/||\__/||\__/||\_ Move from 0 --> 1 || || || || || || || 02 || || 03 || 05 04 01 _/||\__/||\__/||\_ Move from 2 --> 1 || || || || 01 || || 02 || || 03 || 05 04 || _/||\__/||\__/||\_ Move from 0 --> 2 || || || || 01 || || 02 || || 03 || || 04 05 _/||\__/||\__/||\_ Move from 1 --> 0 || || || || || || || 02 || || 03 || 01 04 05 _/||\__/||\__/||\_ Move from 1 --> 2 || || || || || || || || || || 03 02 01 04 05 _/||\__/||\__/||\_ Move from 0 --> 2 || || || || || || || || 01 || 03 02 || 04 05 _/||\__/||\__/||\_ Move from 1 --> 0 || || || || || || || || 01 || || 02 03 04 05 _/||\__/||\__/||\_ Move from 2 --> 1 || || || || || || || || || || 01 02 03 04 05 _/||\__/||\__/||\_ Move from 2 --> 0 || || || || || || || || || 02 01 || 03 04 05 _/||\__/||\__/||\_ Move from 1 --> 0 || || || || || || 01 || || 02 || || 03 04 05 _/||\__/||\__/||\_ Move from 1 --> 2 || || || || || || 01 || || 02 || 04 03 || 05 _/||\__/||\__/||\_ Move from 0 --> 2 || || || || || || || || 01 02 || 04 03 || 05 _/||\__/||\__/||\_ Move from 0 --> 1 || || || || || || || || 01 || || 04 03 02 05 _/||\__/||\__/||\_ Move from 2 --> 1 || || || || || || || || || || 01 04 03 02 05 _/||\__/||\__/||\_ Move from 0 --> 2 || || || || || || || || 03 || 01 04 || 02 05 _/||\__/||\__/||\_ Move from 1 --> 0 || || || || || || || || 03 || || 04 01 02 05 _/||\__/||\__/||\_ Move from 1 --> 2 || || || || || 02 || || 03 || || 04 01 || 05 _/||\__/||\__/||\_ Move from 0 --> 2 || || 01 || || 02 || || 03 || || 04 || || 05 _/||\__/||\__/||\_
-
Alright then, Tower of Hanoi solver in Rust. (See previous post for output.)
- Currently hardcoded to solve the case of 5 pegs on 3 poles, should be fixable. The initial state of the Scene is hardcoded in
Scene::new()
(create poles, stuff pegs on the first pole), and the HanoiSolver is initialized to operate on that state. Scene
is responsible for drawing the current state of the simulation (fromshow()
, usingprintln!()
macro).move_peg()
attempts a valid move of a peg from one pole to another, returning true if it can, or false if it can't. (The solver only attempts valid moves if its set up correctly so this should be a non-issue, but this could support e.g. a human player.)- The scene owns a
Vec
ofPoles
—eachPole
owns aVec
ofPegs
, and tracks the smallest (topmost) Peg. (Not sure thestruct Peg
is buying me much...) - There are a few warnings generated by the code building output strings in
Scene::show()
, but string manipulation in Rust seems to be in a pretty incredible state at the moment anyway. - I'm going as far as to say this is semi-idiomatic code—I'm still adding/removing
mut
/ref
/&
in different places to get things to compile, and breaking objects down in some places to avoid issues with borrowing, but it's starting to feel like it would eventually make sense... - I quite like the
impl Iterator for HanoiSolver
syntax for implementing traits, that seems nice. * TheHanoiSolver
just builds all the states it wants to visit in itsnew
function and stores them in aVec
of (from, to) move co-ordinates, which makes the implementation ofnext()
dirt simple. - Not sure I like the mandatory
self.
on all member variables, I've been usinglet
to try and bind stuff on the LHS toself
on the RHS to avoid this in some places... - Rust is.. very terse..., what with
fn
,mut
,ref
,Vec
,impl
, and so on. I kind of prefer to see full words when I write code, so I'm not sure how fond of this I am. At least they're all fairly self-explanatory abbreviations though. - Don't even ask about
usize
vsu32
(vsi32
...) - That whole thing about using
;
at the end of a block to return()
("void"), vs implicity returning values which don't have a;
after them—that feels completely natural and easy to use, and a lot less complex than I just made it sound...
//------------------------------------------------------------------------------ const PEG_COUNT: usize = 5; const POLE_COUNT: usize = 3; const NO_PEG: u32 = 0xffffffff; struct Peg(u32); struct Pole { pegs: Vec<Peg>, smallest: u32, } struct Scene { poles: Vec<Pole>, } impl Pole { fn new() -> Pole { Pole { pegs: Vec::with_capacity(PEG_COUNT), smallest: NO_PEG, } } fn push(&mut self, p: Peg) -> bool { let Peg(v) = p; if v < self.smallest { self.pegs.push(p); self.smallest = v; true } else { println!("ERR: tried to push size {} onto pole {}.", v, self.smallest); false } } fn pop(&mut self) -> Option<Peg> { let mut smallest = NO_PEG; let mut pegs = &mut self.pegs; let result = pegs.pop(); for i in 0..(pegs.len()) { let Peg(v) = pegs[i]; if v < smallest { smallest = v } } self.smallest = smallest; result } } impl Scene { fn new() -> Scene { let mut result = Scene { poles: Vec::with_capacity(POLE_COUNT), }; for _ in 0..(POLE_COUNT) { result.poles.push(Pole::new()); }; for j in 0..(PEG_COUNT) { result.poles[0].push(Peg((PEG_COUNT - j) as u32)); }; result } fn move_peg(&mut self, from: usize, to: usize) -> bool { if let Some(Peg(v)) = self.poles[from].pop() { if self.poles[to].push(Peg(v)) { return true } else { self.poles[from].push(Peg(v)); } } false } fn get(&self, pole: usize, peg: usize) -> Option<&Peg> { let poles = &self.poles; if poles.len() > pole { let pegs = &poles[pole].pegs; if pegs.len() > peg { return Some(&pegs[peg]) } } None } fn show(&self) { println!(""); for i in 0..(PEG_COUNT) { let mut string = "".to_string(); for j in 0..(POLE_COUNT) { match self.get(j, (PEG_COUNT-1)-i) { Some(&Peg(p)) => { let fmt = format!(" {:02} ", p).to_string(); string.push_str(fmt.as_slice()); } None => { string.push_str(" || "); } } } println!("{}", string); } let mut string = "".to_string(); for _ in 0..(POLE_COUNT) { string.push_str("_/||\\_") } println!("{}", string); println!(""); } } //------------------------------------------------------------------------------ struct HanoiMove { from: usize, to: usize, } struct HanoiState { n: usize, from: usize, to: usize, via: usize, } struct HanoiSolver { step: usize, route: Vec<HanoiMove>, } impl HanoiState { fn new(n: usize, from: usize, to: usize, via: usize) -> HanoiState { HanoiState { n: n, from: from, to: to, via: via } } fn iter(&self, out: &mut Vec<HanoiMove>) { let &HanoiState { n, from, to, via } = self; if n > 0 { HanoiState::new(n - 1, from, via, to).iter(out); out.push(HanoiMove { from: from, to: to }); HanoiState::new(n - 1, via, to, from).iter(out); } } } impl HanoiSolver { fn new(n: usize, from: usize, to: usize, via: usize) -> HanoiSolver { let mut result = HanoiSolver { step: 0, route: Vec::new(), }; let state = HanoiState::new(n, from, to, via); state.iter(&mut result.route); result } } impl Iterator for HanoiSolver { type Item = HanoiMove; fn next(&mut self) -> Option<HanoiMove> { let &mut HanoiSolver { step, ref route } = self; if step < route.len() { let HanoiMove{ from, to } = route[step]; self.step += 1; return Some(HanoiMove { from: from, to: to }) } None } } //------------------------------------------------------------------------------ fn main() { let mut scene = Scene::new(); let mut solver = HanoiSolver::new(PEG_COUNT, 0, 2, 1); scene.show(); while let Some(HanoiMove { from, to }) = solver.next() { println!("Move from {} --> {}", from, to); scene.move_peg(from, to); scene.show(); } }
- Currently hardcoded to solve the case of 5 pegs on 3 poles, should be fixable. The initial state of the Scene is hardcoded in
-
It would probably never have occurred to me to try and put a let in the test expression of an if statement
Actually,if let
has nothing to do with eitherif
orlet
- it's a separate construct more akin tomatch
. AFAIK, it's even implemented to translate intomatch
statement.Personally I find the use of self instead of this to be much more upsetting.
Me too. Also, it's explicit. I'd much rather havestatic fn
andmut fn
member functions. I blame Python for these two.So, typing out a quick paragraph of your distilled understanding of closures gained from (...) was probably a less useful way of convincing them that rust isn't unusable than linking to the actual RFCs and blog posts would have been.
My goal wasn't to convince anyone of anything. I just wanted to explain how closures work.Sure, let's take a word that has been defined and then give it a different definition that isn't related at all and assume people know what we are talking about.
Not related? Enum without fields works exactly like enum in other languages. And enum with fields is so similar to the regular enum they didn't think a separate keyword is necessary.I wasn't that worried about Rust at that point—I was mostly just jumping on gąska for doing the standard tdwtf thing of ignoring good advice just because of who it was coming from.
If it was someone else accusing me of treating everyone around like idiots just because I said "it's simple", I would argue the same way.
-
And enum with fields is so similar to the regular enum they didn't think a separate keyword is necessary.
Except that now you've got two instances that have different contents but are the same in enumeration terms. That is different from every other language out there (that has enums; some don't bother). Taking a term that is very well understood elsewhere and reinterpreting it to mean something else is inevitably a source of confusion, even if that is a job that sometimes needs to be done.
-
Except that now you've got two instances that have different contents but are the same in enumeration terms.
Enum with no fields can be thought a special case of enum with fields.That is different from every other language out there (that has enums; some don't bother).
Rather than different, I'd say generalized for more use cases.
-
Rather than different, I'd say generalized for more use cases.
It's different. Different isn't always bad, but it does make more work for people teaching the language to others.
-
Considering how strict the lifetime system and borrow checking are, tagged-unions-named-enums is the least of teachers' problems. It's the same difficulty as "why the fuck can't I just return int goddammit!!!".
-
I think when it comes to “teachers' problems” right now, the general underdocumentedness of it is a bit more fundamental. ;)
-
Well, lack of documentation is more of a problem for teachees than teachers. If you go to their IRC, there are plenty people there who are very knowledgable about the language - much better source of information than the official docs (sadly; also, currently - might change in future). It's kinda like, before 2011, no C++ tutorial would ever mention shared_ptr, but everyone knew that you should use it instead of raw pointer, always.
-
Well, lack of documentation is more of a problem for teachees than teachers.
I've done some teaching. Lack of documentation is a huge problem…
-
Not if you can keep everything in mind. And, apparently, Rust people can. Which is very impressive, but also raises question about their private lives (or lack of thereof).
-
-
Yeah, my reaction to that revelation too. It would be awesome to do
if let Some(Foo(_, 5)) = x && t > 10
, but no can do.
-
Like a Raspberry Pi! RIGHT GUYS!? RIGHT??? Raspberry Pi!??!???!??!??! LOLOLLOL
Seriously though, why are you defending this shit, Gaska? Everything I've learned about Rust in this thread has led me to the opinion that its an awful language with ass-backwards priorities.
-
Seriously though, why are you defending this shit, Gaska?
I'm not defending. I'm just clarifying. Stating that it's not as much a problem to teach others the language as it is to teach yourself the language is kind of anti-advertisement, don't you think?Everything I've learned about Rust in this thread has led me to the opinion that its an awful language with ass-backwards priorities.
That's because you think everything should be managed. It's not a wrong mindset in general - but it's wrong in Rust.
-
That's because you think everything should be managed.
I think:
-
You shouldn't race headlong into creating the same documentation nightmare of (say) OpenGL. Reproducing their 20-years'-worth of confusion in only a couple years.
-
You shouldn't force the programmer to spend ages thinking about bullshit only the compiler cares about. Unless there's a very very clear case for saving the programmer's time later on.
-
Open source development sucks. The people working on Rust might be good at programming language design, but they're fucking awful at understanding human psychology and that'll doom the language to zero adoption.
It has nothing to do with "everything should be managed", although I guess point 2 could kind of be read that way.
-
-
1) You shouldn't race headlong into creating the same documentation nightmare of (say) OpenGL. Reproducing their 20-years'-worth of confusion in only a couple years.
AFAIK they understand this and they'll have all the docs done by the time of final 1.0 release.2) You shouldn't force the programmer to spend ages thinking about bullshit only the compiler cares about. Unless there's a very very clear case for saving the programmer's time later on.
Managed world doesn't have to deal with uninitialized variables and dangling pointers.3) Open source development sucks.
It's good you put it at the beginning instead of the end - I can safely assume that the rest is prejudicial bullshit and not read it. You know what? I actually read it. And my assumption was right.
-
AFAIK they understand this and they'll have all the docs done by the time of final 1.0 release.
Unless they have a magical brush they can use to scour all the obsolete documentation from the web, this isn't enough.
It's good you put it at the beginning instead of the end
Wow. I knew some cultures read right-to-left but I didn't know any read bottom-to-top.
-
AFAIK they understand this and they'll have all the docs done by the time of final 1.0 release.
Blakey isn't generally interested in the nitty gritty details of causality and temporality and reality.
Unless they have a magical brush they can use to scour all the obsolete documentation from the web, this isn't enough.
See?
-
Unless they have a magical brush they can use to scour all the obsolete documentation from the web, this isn't enough.
Sadly, MIT refused to take down their outdated docs. But I think they could raise the issue to Google itself to not list it on the first page.Wow. I knew some cultures read right-to-left but I didn't know any read bottom-to-top.
- Chinese can be read both left-to-right and right-to-left, and both top-to-bottom and bottom-to-top, and in any combination of the two.
- I was referring to local beginning - ie. the beginning of bullet point. And I only consider this single bullet point prejudicial.
-
Blakey isn't generally interested in the nitty gritty details of causality and temporality and reality.
There's official docs maintained by the original authors of the language, and there's the whole uncontrolled mess of the internets and textbooks. Blaming the authors for what's around the internets is like blaming Obama for school shootings.
-
blaming Obama for school shootings.
I could believe it if we were talking about golf schools.
-
Perfect occasion for negligent discharge.
-
I agree with points 1 and 2, and the second sentence of point 3. It's not “open source” though; just the way certain types of teams work. I would hesitate to claim that it's a special feature of open source; closed teams can get pretty dysfunctional (especially in large organisations) and not all open teams are broken.
-
It's the same difficulty as "why the fuck can't I just return int goddammit!!!".
Actually, as I understand the docs, you can just return int. And it's suggested you can return even large structures by value and it'll be fast - I assume that means that under water the caller is providing the memory space to the callee so no copy is needed.
-
Actually, as I understand the docs, you can just return int.
http://is.gd/zE8sJmBy the time Rust hits 1.0, this warning will become error, with workaround removed completely.
-
Ah, so. Yeah. Thought you were referring to the whole problem with returning references with unknown lifetimes.
-
I just wanted to explain how closures work.
If someone here is having a hard time getting to the bottom of something, it is generally a safe bet that they are not struggling with the simple part of it.
-
If someone here is having a hard time getting to the bottom of something, it is generally a safe bet that they are not struggling with the simple part of it.
I have experienced, on both sides of the equation, situations where some simple assumption was incorrect and causing the problems. Once pointed out, things became much clearer.
-
I'll have another look for Rust docs over lunch. If I find anything, I'll follow up...
-
Rust docs
@tar said:TDEMSYR
OK, I read over the Ownership section of the Rust Book, and I managed to make my closures work right...
fn make_closure<'a>(start: i32) -> Box<FnMut(i32) -> i32 + 'a> { let mut v = start; box move |&mut:a: i32| -> i32 { let t = v + a; println!("{} + {} = {}", v, a, t); v = t; t } }
EDITED to fix return value
-
Awesome. Although, apparently
|&mut:|
is now obsolete; they want you to let them infer the closure type all by their self.
-
they want you to let them infer the closure type all by their self.
Bad programmer! Stop thinking! BAD!
-
box move |&mut i32| -> i32 {
Your lambda doesn't return anything, yet you declared it to return i32. Did you even try to compile your code?Bad programmer! Stop thinking! BAD!
Well, the syntax was very ugly. Even for natively compiled languages standards.
-
Well, the syntax was very ugly. Even for natively compiled languages standards.
True. But instead of making syntax nicer they decided they'll just fix it in post
-
Except the feature wasn't supposed to be there at all in the first place - it was meant to be all inferred from the start, but the initial implementation wasn't working in all cases and thus an ugly hack was implemented.
-
Your lambda doesn't return anything, yet you declared it to return i32. Did you even try to compile your code?
Of course I compiled it. Isn't
v = t
(i.e. the value oft
) the return value? That's what I would've expected...
-
It isn't, for two reasons - assignment returns (), not the value, and you put a semicolon at the end.
-
It isn't, for two reasons
Oh. I wonder why that's compiling...
assignment returns (), not the value
That's stupid.and you put a semicolon at the end.
I'm stupid.I think I did have
v = t; t
originally.Seriously though, no way to take the value of an assigment? Fuckin' nanny state programming languages these days...
OK, I edited in a solitaty
t
at the end of the closure. (Not that we're actually using the return value for anything anyway...)
-
That's stupid.
It's not. Value-y assignment exists exclusively in C and only leads to either extremely unreadable code or nasty little bugs. Did you ever have to deal with a code that uses assignment in if condition, where it's not a typo but actually the intended behavior? I did.It made sense in C because that's how assembler works. We're long past assembler-as-general-purpose-language times now.
-
My problem with it is that if we keep removing or neutering language features because some hypothetical idiot might have trouble with them, then eventually we end up with no language at all.
We can invent a hypothetical idiot who has trouble with anything. At some point you have to just say, damn it, let's just assume everyone worth supporting will reach a level of competence, and stop giving experienced users the shaft for the perceived benefit of 'new users'.
See PHP for an example of the logical endpoint of that thought process...
-
Value-y assignment exists exclusively in C
Did you ever have to deal with a code that uses assignment in if condition
-
OK, here's a fun little Rust puzzle. This code doesn't compile, but why? Does it not compile for valid reasons or invalid reasons? DISCUSS!
fn main() { let a = 1; let mut b = 2; let c = (b = a); println!("{} {} {}", a, b, c); }
(Someone remind me to print the compiler error before I go to work, or we'll be here all day...)
-
My problem with it is that if we keep removing or neutering language features because some hypothetical idiot might have trouble with them, then eventually we end up with no language at all.
No, we end up with better, less buggy, software.
We can invent a hypothetical idiot who has trouble with anything. At some point you have to just say, damn it, let's just assume everyone worth supporting will reach a level of competence, and stop giving experienced users the shaft for the perceived benefit of 'new users'.
Right; but that assumed level of competence has to include everybody who will touch your codebase. And a lot of companies hire idiots.
See PHP for an example of the logical endpoint of that thought process...
PHP is pretty much the exact opposite philosophy.
-
My problem with it is that if we keep removing or neutering language features because some hypothetical idiot might have trouble with them, then eventually we end up with no language at all.
That's not exactly wrong, but there's not really anything hypothetical with this situation.
-
Right; but that assumed level of competence has to include everybody who will touch your codebase. And a lot of companies hire idiots.
My proposed solution is to start my own company which doesn't hire idiots. With blackjack! And hookers!
PHP is pretty much the exact opposite philosophy.
I don't know, I thought the PHP developers were on record as being primarily concerned with making things 'accessible'? Which is why PHP is really easy to get started with, but has all kinds of crazy second-order behaviours once features of the language start interacting with each other in unanticipated ways...
-
PHP may have started that way, but its problems really started when they went for the "kitchen sink" philosophy and just started throwing everything in there.
-
My problem with it is that if we keep removing or neutering language features because some hypothetical idiot might have trouble with them, then eventually we end up with no language at all.
Removing language features can have one of three outcomes:- Crippling functionality of the language - because what was possible (or equivalently, easier to do) before, now is impossible (or very hard).
- Cleaning up the language - because the functionality wasn't very good anyway, or rarely useful, or collided with another potential feature.
- Make the users write better, more readable code - in this category lies goto, value-y assignments and value-y increments.
OK, here's a fun little Rust puzzle. This code doesn't compile, but why? Does it not compile for valid reasons or invalid reasons? DISCUSS!
The compile error is clear enough. Not a particularly hard puzzle if you're allowed to use compiler.
-
The compile error is clear enough. Not a particularly hard puzzle if you're allowed to use compiler.
I got this error, and it hurt me:
error: the trait `core::fmt::String` is not implemented for the type `()`
I'm assuming that that's something they're going to fix at some point? Having my logging code fail to compile because the value I'm trying to log didn't exist doesn't seem like a happy place to be...
-
You got it wrong. The value exists. The value is (), of type (), returned by expression "b = a", assigned to variable c. The println! macro depends on types having core::fmt::String trait implemented, and () type doesn't implement it. This is the cause of error.