These are chat archives for rust-lang/rust

2nd
Feb 2019
Benjamin Halsted
@halzy
Feb 02 01:15
Hello, I'm trying to use a range, with iterator and map to make a string. I'm not sure what is the best way. I'd like something similar to:
(1..5).iter().map(|i| format!("!{}!", i).join(",".to_string())
In order to get: !1!,!2!,!3!,!4!,!5!
(Fixed to avoid # links)
Kelly Thomas Kline
@kellytk
Feb 02 01:34
Regarding basic error handling, if working with libs such as Rusqlite, should Rusqlite::Error be converted to failure::Error eagerly or should functions return whatever error types that originate in them?
David O'Connor
@David-OConnor
Feb 02 14:51
Hey dudes. Does anyone know how to convert an arbitrary struct or vec to a web_sys::JsValue ?
David O'Connor
@David-OConnor
Feb 02 15:04
edit: Looks like you can do it with a serialized data structure using the from_serde method. I'm suspicious this is the only oway
octave99
@octave99
Feb 02 17:18
Is there something available to split a slice or vector using delimiter of same type? Like [1,2,3,4,5,6].split([3,4]). Unable to identify anything relevant here: https://doc.rust-lang.org/std/vec/struct.Vec.html
Arnaud Esteve
@aesteve
Feb 02 19:57

Hi everyone, I'm trying to play around with actix::web and especially this issue : actix/actix-web#310 . One solution would be to have something "like" this :

    server::new( || {
        let mut open_api = OpenApiDefinition {
            name: "My Hello World API".to_owned(),
            description: "The description of the API".to_owned(),
            operations: Vec::<Operation>::new(),
        };
        App::new()
            .middleware(Logger::default())
            .resource("/hello/{name}", |r| {
                open_api.operations.push(Operation {
                    name: "Just saying hello".to_owned(),
                    method: "GET".to_owned()
                });
                r.method(Method::GET).f(say_hello)
            })
            .resource("/swagger.json", |r| {
                r.method(Method::GET).f(|_req| -> Result<Json<OpenApiDefinition>> {
                    Ok(Json(open_api))
                })
            })
    }).bind("127.0.0.1:9007").unwrap().run();

But it's not working, since closures would need to take ownership of open_api one after another.
I've read several times the chapters about mutability and ownership, but still, I cannot understand how to solve such problem.

in this case the "scope" of open-api is pretty hard to determine for me.
That's something constructed by the user in closures, then which should be available at runtime within a "route".
Arnaud Esteve
@aesteve
Feb 02 20:13
I've also read about Rc and RefCell, even Arc somewhere later in the book.
But it doesn't help since I'm probably not using it well.
I've tried moving let mut open_api out of the server.new( closure, (and then use Arc as it's mentioned it needs to be thread safe, but still, I'm going round in circles between "you need to move open_api", "you should use mut open_api", "you should remove mut open_api", "the argument must outlive static"
Denis Lisov
@tanriol
Feb 02 20:16
One option that will work is to wrap the struct into Arc<Mutex<OpenApiDefinition>> and make for every closure a separate clone of it. This one is not actix-specific, however. There may be better ways in actix-web.
Arnaud Esteve
@aesteve
Feb 02 20:17
I'd be happy with the most generic solution, since I'm trying to learn through the first example I found interesting
thank you, I'll give this a try
in your opinion, where should the let open_api definition be scoped ? Within main (top-level) or inside the server::newdeclaration ?
Denis Lisov
@tanriol
Feb 02 20:29
Depends on whether there may be a valid reason to customize it before starting.
The actix-web way is to use App::with_state and get it with r.state() after that, but you'd still need Arc for sharing between threads if you need that and a Mutex for shared mutable data.
Arnaud Esteve
@aesteve
Feb 02 20:32

mmmh, almost there I hope:

    let open_api = Arc::new(Mutex::new(OpenApiDefinition {
        name: "My Hello World API".to_owned(),
        description: "The description of the API".to_owned(),
        operations: Vec::<Operation>::new(),
    }));
    server::new( move || {
        App::new()
            .middleware(Logger::default())
            .resource("/hello/{name}", |r| {
                let mut open_api = open_api.lock().unwrap();
                open_api.operations.push(Operation {
                    name: "Just saying hello".to_owned(),
                    method: "GET".to_owned()
                });
                r.method(Method::GET).f(say_hello)
            })
            .resource("/swagger.json", |r| {
                r.method(Method::GET).f(|_req| -> Result<Json<OpenApiDefinition>> {
                    let open_api = open_api.lock().unwrap();
                    Ok(Json(open_api.clone()))
                })
            })
    }).bind("127.0.0.1:9007").unwrap().run();

Still does not compile.
^ argument requires that'1must outlive'static``. So, the thing it's missing (I guess) is: how can I "get the value out of the mutex, maybe clone it, or at least do something that allows it to be usable at runtime when someone will execute an HTTP get on this endpoint"

sorry, didn't see your earlier answer. I've read the example in actix-web with the mutex and the counter within the state. (I didn't manage to make it compile locally but I didn't try hard enough). But I turned away, because I realised that in route declaration, I don't need the State.
and then, combining both approaches:
Arnaud Esteve
@aesteve
Feb 02 20:38
    let open_api = Arc::new(Mutex::new(OpenApiDefinition {
        name: "My Hello World API".to_owned(),
        description: "The description of the API".to_owned(),
        operations: Vec::<Operation>::new(),
    }));
    server::new( move || {
        App::with_state(open_api)
            .middleware(Logger::default())
            .resource("/hello/{name}", |r| {
                let mut open_api = open_api.lock().unwrap();
                open_api.operations.push(Operation {
                    name: "Just saying hello".to_owned(),
                    method: "GET".to_owned()
                });
                r.method(Method::GET).f(say_hello)
            })
            .resource("/swagger.json", |r| {
                r.method(Method::GET).f(|req| -> Result<Json<OpenApiDefinition>> {
                    let open_api = req.state().lock().unwrap();
                    Ok(Json(open_api.clone()))
                })
            })
    }).bind("127.0.0.1:9007").unwrap().run();
Still doesn't get me anywhere.
I must be missing something obvious, but really, I cannot understand ๐Ÿ˜–
Arnaud Esteve
@aesteve
Feb 02 21:08
Well, I'm giving up ๐Ÿ˜” . I'll get back to it one day I fully understanding what moving really involves. Here I got compiler errors I don't understand. Still to early in "the book".
Denis Lisov
@tanriol
Feb 02 21:28

@aesteve The point you did not take into account was that you need to have separate clones of the Arc for separate closures. You need to explicitly clone it like

let api_clone = Arc::clone(open_api);
// ...
App::new()
    .resource("/path", |r| {
        let api = api_clone.lock().unwrap();
        // ...
    })

This is usually not very readable, so I had a macro somewhere to simplify it.

Arnaud Esteve
@aesteve
Feb 02 21:33
I tried it (I almost tried every possible combination of &, clone, move and mut ๐Ÿ˜…) but there's always this "must outlive static" stuff I do not how to deal with
latest try:
    let open_api = Arc::new(Mutex::new(OpenApiDefinition {
        name: "My Hello World API".to_owned(),
        description: "The description of the API".to_owned(),
        operations: Vec::<Operation>::new(),
    }));
    let api_clone = Arc::clone(&open_api);
    server::new( move || {
        App::with_state(AppState {open_api: open_api.clone()})
            .middleware(Logger::default())
            .resource("/hello/{name}", |r| {
                api_clone.lock().unwrap().operations.push(Operation {
                    name: "Just saying hello".to_owned(),
                    method: "GET".to_owned()
                });
                r.method(Method::GET).f(say_hello)
            })
            .resource("/swagger.json", |r| {
                r.method(Method::GET).f(|req| -> Result<Json<OpenApiDefinition>> {
                    Ok(Json(req.state().open_api.lock().unwrap().clone()))
                })
            })
    }).bind("127.0.0.1:9007").unwrap().run();
Denis Lisov
@tanriol
Feb 02 21:34
Have you tried move |r| too?
Arnaud Esteve
@aesteve
Feb 02 21:34
yup
39 |       let api_clone = Arc::clone(&open_api);
   |           --------- captured outer variable
...
43 |               .resource("/hello/{name}", move |r| {
   |  ________________________________________^
44 | |                 api_clone.lock().unwrap().operations.push(Operation {
45 | |                     name: "Just saying hello".to_owned(),
46 | |                     method: "GET".to_owned()
47 | |                 });
48 | |                 r.method(Method::GET).f(say_hello)
49 | |             })
   | |_____________^ cannot move out of captured variable in an `Fn` closure
Denis Lisov
@tanriol
Feb 02 21:35
Okay, that's kinda fun :-) let me check my understanding...
Arnaud Esteve
@aesteve
Feb 02 21:36
somehow, I' must say I feel a bit comforted ๐Ÿ˜…
Denis Lisov
@tanriol
Feb 02 21:45
Well, these are sometimes non-obvious :-)
Due to actix-web design choice, Api::clone has to be done inside the server::new closure. And the |r| ones need to be move.
Denis Lisov
@tanriol
Feb 02 21:52
Passing all that state by hand is quite tedious, yes.
Arnaud Esteve
@aesteve
Feb 02 21:54
oh wow, moving the clone clone 2 lines below indeed compiles
but the .resource must have been called 8 times I guess, didn't expect that (1 per cpu-core or something ?)
Capture dโ€™eฬcran 2019-02-02 aฬ€ 22.57.36.png
Denis Lisov
@tanriol
Feb 02 21:57
Yes, the initialization function is called once per server thread, that's why it's important :-)
Arnaud Esteve
@aesteve
Feb 02 21:58
ok, so that's not the right design, at least I know ! Thank's a lot
that's a quite interesting github issue, way harder than I thought it'd be.