These are chat archives for rust-lang/rust

9th
Jun 2017
Scott Corbeil
@scorbeil
Jun 09 2017 12:20

I am getting a compiler error, but I have a different issue than I thought yesterday. Sorry, I was a bit burned out from chasing bugs and ended up chasing the wrong problem. The real issue is more similar to this:

trait Foo<T> {
    fn method(self) -> T;
}

enum Things<T> {
    A(T),
    B(T),
}

impl<T> Foo<T> for Things<T> {
    fn method(self) -> T {
        unimplemented!()
    }
}

impl<T> Things<T> {
    fn change(&mut self, t: T) {
        match *self {
            Things::A(_) => *self = Things::B(self.method(t)),
            Things::B(_) => *self = Things::A(self.method(t)),
        }
    }
}

I think the problem is that my Foo implementation consumes self, so I can't call self.method() inside of the change method. At all. That is frustrating me, because I don't think I can change the Foo trait, for other reasons. I don't suppose there's any way around this?

Daan Rijks
@ijks
Jun 09 2017 12:27
You could implement/derive Clone for Things, then you can do self.clone().method(). But why does Foo::method() have to take self?
Scott Corbeil
@scorbeil
Jun 09 2017 12:33
I suppose it isn't required. It's just nice. Foo is actually a state trait and method, in this example, is the step method which produces the next state. I liked that it consumed self, ensuring that old states didn't hang around. If it isn't obvious, I'm still very much struggling to get into the Rust mindset. That's part of the reason I'm building this project. It's sort of a learning exercise.
Scott Corbeil
@scorbeil
Jun 09 2017 12:57
I think that while implementing Clone for the trait might solve this problem, it defeats the purpose of the states consuming themselves, so I might as well just bite the bullet and change Foo::method() to method(&self) :(
Daan Rijks
@ijks
Jun 09 2017 13:07
Hm, I'd say it does make sense to have it take self, if you want to guarantee the old state isn't used. I take it change() is meant to define all possible state transitions?
Wait no, that's method() right?
In any case, I think there are some articles around about different ways you can do state machines in Rust.
Scott Corbeil
@scorbeil
Jun 09 2017 13:09
You know what, this example is rather oversimplified and makes it difficult to discuss further. If you don't mind reading ~150 LOC, I'll paste the relevant parts in a Gist or something.
Either way, I appreciate the assistance so far :)
Scott Corbeil
@scorbeil
Jun 09 2017 13:20

If you're interested in spending the time, here are the relevant bits. Link [1] is the file containing the definition of the state trait and several reference implementations that adapt fn pointers and closures to be used as states. This file compiles fine. Link [2] is the file containing the definition of the machine trait, which is intended to be an interface for objects that take states and run them, optionally producing some output (tokens, etc). This file does not compile, for the reasons discussed above.

My end goal is to build an iterator adapter that takes a machine implementation and produces an iterator of tokens so you could do something like iter.tokenize(machine), but I'm trying to keep it as modular and generic as possible. That way, the pieces can be used for other purposes.

[1] https://gist.github.com/scorbeil/1f88d03a52029328412629ca7dfbff58
[2] https://gist.github.com/scorbeil/7081a597e7453e632a59ccf9db78d023

Oh man, I didn't realize that would render the gist in place. Sorry for the clutter.
Scott Corbeil
@scorbeil
Jun 09 2017 13:26
Btw, the run() method in that second link is an absolute mess, I realize. I fought with it for like a day before I realized what the actual underlying problem was.
Daan Rijks
@ijks
Jun 09 2017 13:36
So where are you getting the error? In run()?
Scott Corbeil
@scorbeil
Jun 09 2017 13:36
Yes.
I finally figured out it's because of the call to s.step(c)
Daan Rijks
@ijks
Jun 09 2017 13:41
Hm... Maybe you could have run() take self as well?
Scott Corbeil
@scorbeil
Jun 09 2017 13:42
I thought about doing that. The reason I didn't is because then the machine would consume itself, too. You're supposed to be able to call run() repeatedly to get a piece of output (token?) each time.
Daan Rijks
@ijks
Jun 09 2017 13:44
Ah, I see.
Scott Corbeil
@scorbeil
Jun 09 2017 13:44
I mean, I guess I could have the machine return itself as well, similar to the way the states are doing here, but that just seems like jumping through hoops. The states consume themselves because they are likely to return a different state. The machine would just be returning itself every time...
I think I'm ultimately going to change the states to take &self. I don't like it, but it would probably be necessary anyway to enable the use of these tools to create a PDA, and my intention was to be agnostic to the type of machine (hence the heavy use of generics)
PDA states wouldn't consume themselves in the case of a pop.
Er, push, rather
Daan Rijks
@ijks
Jun 09 2017 14:00
Skimming through it, it definitely looks like the kind of thing you're trying to do.
Scott Corbeil
@scorbeil
Jun 09 2017 14:00
Thank you. I've actually read that (and several other articles).
Daan Rijks
@ijks
Jun 09 2017 14:02
I'm afraid I can't really help you any further then. :sweat_smile:
Scott Corbeil
@scorbeil
Jun 09 2017 14:02
I have reasons for not just copy/pasting hoverbear's implementation. One of them being that his example seems more suited for the type of machine that might implement (for example) network protocols, Raft, coin-machine-like solutions, etc. As opposed to the token-emitting, lexer-style that I'm going for.
I think you've helped a great deal, thanks.
Daan Rijks
@ijks
Jun 09 2017 14:02
You're welcome. :)
Denis Lisov
@tanriol
Jun 09 2017 14:10
@scorbeil Why do you need the enum FSM and not just struct FSM { state: Option<State> }?
You never store Both or Output anyway...
Scott Corbeil
@scorbeil
Jun 09 2017 14:16
Okay, I see why you're asking. The idea was that, if I were writing a state as - say - a static function, I could do this:
fn state_a(&mut Scanner) -> (Option<State>, Option<Token>) {
    let symbol = Scanner.Peek();
    match symbol {
        something_relevant => return (state::StateFnItem(state_b), None),
        _ => // etc...
    }
}

fn state_b(&mut Scanner) -> (Option<State>, Option<Token>) {
    /* something similar */
}
Does that make sense?
Denis Lisov
@tanriol
Jun 09 2017 14:18
You still could do that. My point here is that in the impl Machine for FSM you always return the output right away, thus you don't need to store it in the FSM...
...anyway, even if you need, the output and state seem pretty much orthogonal, so I'd go with struct FSM { state: Option<S> , output: Option<O>}
Scott Corbeil
@scorbeil
Jun 09 2017 14:20
Oh, okay. I get what you're saying. I'll probably make that change, but it doesn't fix the issue from above. The issue above was that I'm storing the state in the machine and then trying to call .step(c) on it, which consumes the state... can't do that since it's borrowed.
You do make a good point, though.
Denis Lisov
@tanriol
Jun 09 2017 14:27
And then you can do something like this:
let state = self.state.take();
match state {
    Some(state) => {
        let (new_state, output) = state.step();
        self.state = new_state;
        if let Some(output) = output {
            return Some(output);
        }
    },
    None => return None
}
Scott Corbeil
@scorbeil
Jun 09 2017 14:30
I need to look up .take(). Still not 100% familiar with std lib
Denis Lisov
@tanriol
Jun 09 2017 14:31
Option::take: Takes the value out of the option, leaving a None in its place.
Elías Josué Puma Chávez
@ZeroBlazer
Jun 09 2017 14:32
Hello everyone, I'm new in rust and I'm trying to implement a database that holds multiple records in a vector, I got to a point where I need to use a generic for each record I use, so I'm using a nested generic trait (don't know if that's the right way to call it) but I can't make it work.
My lib code
The error I get:
error[E0392]: parameter `U` is never used
  --> /run/media/rustycode/Storage_ext4/Dev/db-topics/libs/database/src/generic.rs:70:24
   |
70 | pub struct Database<T, U>
   |                        ^ unused type parameter
   |
   = help: consider removing `U` or using a marker such as `std::marker::PhantomData`

error: aborting due to previous error
Denis Lisov
@tanriol
Jun 09 2017 14:33
@ZeroBlazer Please show the Database definition
Scott Corbeil
@scorbeil
Jun 09 2017 14:33
@tanriol oh man, that's perfect. That fixes the whole thing, I think.
Elías Josué Puma Chávez
@ZeroBlazer
Jun 09 2017 14:34
#[derive(Debug)]
pub struct Database<T, U>
    where T: Record<U>
{
    data: Vec<T<U>>,
    abs_sd: Vec<(f32, f32)>,
}
Scott Corbeil
@scorbeil
Jun 09 2017 14:34
Thank you!
Elías Josué Puma Chávez
@ZeroBlazer
Jun 09 2017 14:34
I tried changing Vec<T<U>> for Vec<T> only
Denis Lisov
@tanriol
Jun 09 2017 14:34
@scorbeil It could be done even for the previous version with mem::replace, but with more boilerplate.
Elías Josué Puma Chávez
@ZeroBlazer
Jun 09 2017 14:35
but it won't work :/
Denis Lisov
@tanriol
Jun 09 2017 14:37
@ZeroBlazer I'd try using Vec<T> and adding another field: _output: PhantomData<U>
Elías Josué Puma Chávez
@ZeroBlazer
Jun 09 2017 14:39
Awesome, I think it'll work now
I just started using generic traits
Elías Josué Puma Chávez
@ZeroBlazer
Jun 09 2017 14:48
It worked! :) Thank you so much
Scott Corbeil
@scorbeil
Jun 09 2017 16:10
Hey @tanriol , it compiles! Thanks again! If you're curious, I remembered why I had the enum, and fixed it. Here's the final product:
impl<C, S, O> Machine for FSM<C, S, O> where S: state::State<C=C, R=FSM<C, S, O>> {
    type C = C;
    type O = O;

    fn run(&mut self, c: &mut Self::C) -> Option<Self::O> {
        loop {
            match mem::replace(self, FSM::Nothing) {
                FSM::State(s) => {
                    mem::replace(self, s.step(c));
                }
                FSM::Output(o) => return Some(o),
                FSM::Both(s, o) => {
                    mem::replace(self, FSM::State(s));
                    return Some(o);
                }
                FSM::Nothing => return None,
            }
        }
    }
}
Note that I changed the definition of R in the impl