Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implements Room.findPath() #53

Merged
merged 7 commits into from
Sep 25, 2018
Merged

Implements Room.findPath() #53

merged 7 commits into from
Sep 25, 2018

Conversation

ASalvail
Copy link
Collaborator

I'm very unsure of this one. For some obscure reasons (that might end up in our favor?!), Room.findPath() uses a mutable reference to the cost matrix to make its changes. I adapted the code in that direction, but kept to the scoped_lts approach.

However, since a CostMatrix is received as input, it doesn't need lifetime annotations anymore. The thing is, I don't know enough about the intricacies of rust <-> wasm interractions to actually make a judgement call about whether this is still necessary.

I'm all ear to learn about it however 😄

@ASalvail
Copy link
Collaborator Author

Correct me if I'm wrong, but the whole shenanigans with the closure that was needed for the pathfinder::search was due to the CostMatrix being generated from the callback, thus making it's lifetime hard to infer by the rust compiler.

In that case, implementing a callback for map.findRoute should not need any of that and simply use the callback functionalities provided by stdweb. Is that correct?

@daboross
Copy link
Collaborator

Unfortunately the lifetime shenanigans are related to having a closure at all that we pass into JavaScript which is not 'static. I think we'll need it any time we want to have full capability in callbacks.

Copy link
Collaborator

@daboross daboross left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the lack of lifetime annotations is correct- JS owns a CostMatrix, we have a reference to it. JS's CostMatrix will last as long as we want since we have a reference so it doesn't need a lifetime.

Here are a few other comments just about the current state of this.

where
O: HasPosition,
T: HasPosition,
F: Fn(String, CostMatrix) -> (),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function should be able to return a CostMatrix in order to overwrite the one provided by the room- this probably wants to be -> Option<CostMatrix>.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honnestly, my first thought was not to support replacing the cost matrix. It is both inneficient and a pain to implement.

screeps-game-api/src/constants.rs Show resolved Hide resolved
};
use HasPosition;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you feel like cleaning up the use style, this should be in the objects:: group. It's probably not correct anywhere else anyways though until #49 is merged.

from_pos: &O,
to_pos: &T,
opts: FindOptions<F>,
) -> Vec<Step>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only handles non-serialized paths.

Idea:

We could make FindOptions generic over "F: FindResultType", a trait like:

trait FindResultType {
    type Result;

    fn deserialize(v: Value) -> Result;
    fn serialized() -> bool;
}

then implement it for RegularPath, SerializedPath (or names you choose). This would let us return F::Result from find_path and handle both regular results which are Vec<Step>s and serialized results which are Strings.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be easier just to make a Path enum with a SerializedPath(String) and a VectorizedPath(Vec<Step>) variant?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be easier, but it then someone who only wants non-serialized paths would have to unwrap the result. Since it's in our power to give type-system-level assurance that the path is a given type, I'd rather do that than require extra runtime checks.

I mean it's fine if we don't handle non-serialized paths for now as long as we don't allow someone to set the 'serialized' FindOptions setting.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having an enum is still good though! I'd just rather have the type-level assurance that the user gets the correct type as well.

I've preemptively opened #61 for implementing this after this PR is merged.

@ASalvail
Copy link
Collaborator Author

Unfortunately the lifetime shenanigans are related to having a closure at all that we pass into JavaScript which is not 'static. I think we'll need it any time we want to have full capability in callbacks.

In the closure example from stdweb's doc, none of this is mentioned. I don't believe that it the closure there is anymore static. I'm confused about whether it's a document failure on their end or if it's a problem particular to our use case that requires the whole unpacking of the callback.

@daboross
Copy link
Collaborator

daboross commented Sep 24, 2018

You're right, it isn't mentioned there. The page doesn't explicitly mention that you can use non-static data either, though. It just mentions "directly embedding rust code" which is fully doable with only static closures.

I think I came to the conclusion just by testing it out and seeing that it didn't work. For example, if you modify the main page's example to use non-static data, it will fail:

#[macro_use]
extern crate stdweb;

fn main() {
    let outer_owned_variable = "Goodbye.";
    let print_hello = |name: String| {
        println!("Hello, {}! {}", name, &outer_owned_variable);
    };

    js! {
        var print_hello = @{print_hello};
        print_hello( "Bob" );
        print_hello.drop(); // Necessary to clean up the closure on Rust's side.
    }
}
$ cargo check
    Checking stdweb-test v0.1.0 (/home/daboross/stdweb-test)                                                                                                 
error[E0597]: `outer_owned_variable` does not live long enough                                                                                               
  --> src/main.rs:7:42                                                                                                                                       
   |                                                                                                                                                         
6  |     let print_hello = |name: String| {                                                                                                                  
   |                       -------------- value captured here                                                                                                
7  |         println!("Hello, {}! {}", name, &outer_owned_variable);                                                                                         
   |                                          ^^^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough                                                  
...                                                                                                                                                          
15 | }                                                                                                                                                       
   | - `outer_owned_variable` dropped here while still borrowed                                                                                              
   |                                                                                                                                                         
   = note: borrowed value must be valid for the static lifetime...                                                                                           
                                                                                                                                                             
error: aborting due to previous error                                                                                                                        
                                                                                                                                                             
For more information about this error, try `rustc --explain E0597`.                                                                                          
error: Could not compile `stdweb-test`.                                                                                                                      

To learn more, run the command again with --verbose.

We could mirror the stdweb behavior and not accept closures with borrowed data. This would be fully doable, we could eliminate all of the extra code and greatly simplify PathFinder bindings.

If we did this, though, our bindings would not allow closures which borrow local data. I made the decision to do all of the extra stuff simply because using borrowed data is generally a useful thing to do.

@ASalvail
Copy link
Collaborator Author

Now that you mention it, I remember you telling me exactly this, but I suppose I'm a very forgetful person!

This is an amazing explanation however, we should put it in the documentation somewhere...

@ASalvail
Copy link
Collaborator Author

Waiting for a second pass through the code.

Copy link
Collaborator

@daboross daboross left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good- good work on the callback bindings!

I've written a few notes, then I think we do need a custom Deserialize implementation for Direction.

screeps-game-api/src/objects/impls/room.rs Show resolved Hide resolved

#[derive(Debug, Deserialize)]
pub enum Path {
VectorizedPath(Vec<Step>),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Path in the variant names seems redundant here- it's already in the Path enum?

I think Path::Vectorized would be just as clear as Path::VectorizedPath.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very good idea.

@daboross
Copy link
Collaborator

If you want, I could merge this PR as is and just merge #56 on top of it afterwards. Might be nice in order to have this with all the comments officially marked as "merged"?

@ASalvail ASalvail changed the title [For review, not to be merged] Implements Room.findPath() Implements Room.findPath() Sep 25, 2018
@ASalvail
Copy link
Collaborator Author

Please do, I'll finish #56 later

@daboross
Copy link
Collaborator

👍

thanks!

@daboross daboross merged commit 8f20cfe into rustyscreeps:master Sep 25, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants