gsserge's comments

gsserge | 3 years ago | on: Germany boosts renewables with “biggest energy policy reform in decades”

> It was not nearly as obvious 15 years ago, as it is today, but all the signs were there. We (I include myself here) were just not listening closely enough to the countries that knew Russia the best: Estonia, Latvia, Lithuania, Poland, Ukraine.

After living in Western Europe for almost a decade it's still shocking to me how much people here do not understand Russia.

gsserge | 4 years ago | on: Ask HN: Who is hiring? (July 2021)

Fiberplane | Rust Engineer, Frontend Engineer, Solution Engineer | Amsterdam, The Netherlands | ONSITE or REMOTE (CET +/- 3 hours) At Fiberplane (https://fiberplane.dev), we're redefining the future of collaboration for infrastructure teams, starting with collaborative notebooks for resolving incidents. Our vision is to put a programmable SRE environment at the fingertips of engineers everywhere.

We previously built Wercker, a container native CI/CD platform that was acquired by Oracle.

Our current open positions:

- Rust Engineer https://fiberplane.dev/careers/rust-engineer

- Frontend Engineer https://fiberplane.dev/careers/frontend-engineer

- Solution Engineer https://fiberplane.dev/careers/solution-engineer

- Developer Advocate https://fiberplane.dev/careers/developer-advocate

- Customer Experience Specialist https://fiberplane.dev/careers/customer-experience-specialis...

Want to join us? Email martina(at)fiberplane.com. You're more than welcome to get in touch if you're interested in what we're building but don't match the above open positions.

gsserge | 4 years ago | on: Ask HN: Who is hiring? (May 2021)

Fiberplane | Rust Engineer & Solution Engineer | Amsterdam, The Netherlands | ONSITE or REMOTE (CET +/- 3 hours)

At Fiberplane (https://fiberplane.dev), we're redefining the future of collaboration for infrastructure teams, starting with collaborative notebooks for resolving incidents. Our vision is to put a programmable SRE environment at the fingertips of engineers everywhere.

We previously built Wercker, a container native CI/CD platform that was acquired by Oracle.

Our current open positions:

- Rust Engineer https://fiberplane.dev/careers/rust-engineer

- Solution Engineer https://fiberplane.dev/careers/solution-engineer

Want to join us? Email martina(at)fiberplane.com. You're more than welcome to get in touch if you're interested in what we're building but don't match the above open positions.

gsserge | 5 years ago | on: React and D3.js

visx is great! It hides a lot of D3 complexity and provides a very composable React API. The D3 API are still there if and when you need them.

gsserge | 5 years ago | on: Why asynchronous Rust doesn't work

Oh yeah, I can totally relate to the Send-Sync-Unpin massaging, plus 'static bound for me. It's so weird that individually each of them kinda makes sense, but often you need to combine then and all of a sudden the understanding of combinations just does not.. combine. After a minute or two of trying to figure out what should actually go into that bound I give up, remove all of them and start adding them back one by one until the compiler is happy.

gsserge | 5 years ago | on: Why asynchronous Rust doesn't work

Thanks for the suggestion. I didn't think of (1), although it's a pity that it's not as ergonomic as async fn.

I kinda feel like there's this false dichotomy here: either hide and be like Java/Go or be as explicit as possible about the costs like C/C++. Is there maybe a third option, when I as a developer aware of the allocation and dispatch costs, but the compiler will do all the boilerplate for me. Something like `async dyn fn(Request) -> Result<Response>`? :)

gsserge | 5 years ago | on: Why asynchronous Rust doesn't work

I think Rust is also able to hide certain things. Without async things are fine:

    type Handler = fn(Request<Body>) -> Result<Response<Body>, Error>; 
    let mut map: HashMap<&str, Handler> = HashMap::new(); 
    map.insert("/", |req| { Ok(Response::new("hello".into())) }); 
    map.insert("/about", |req| { Ok(Response::new("about".into())) });
Sure, using function pointer `fn` instead of one of the Fn traits is a bit of a cheating, but realistically you wouldn't want a handler to be a capturing closure anyway.

But of course you want to use async and hyper and tokio and your favorite async db connection pool. And the moment you add `async` to the Handler type definition - well, welcome to what author was describing in the original blog post. You'll end up with something like this

    type Handler = Box<dyn Fn(Request) -> BoxFuture + Send + Sync>; 
    type BoxFuture = Pin<Box<dyn Future<Output = Result> + Send>>;
plus type params with trait bounds infecting every method you want pass your handler to, think get, post, put, patch, etc.

    pub fn add<H, F>(&mut self, path: &str, handler: H)
    where
        H: Fn(Request) -> F + Send + Sync + 'static,
        F: Future<Output = Result> + Send + 'static,
And for what reason? I mean, look at the definitions

    fn(Request<Body>) -> Result<Response<Body>, Error>;
    async fn(Request<Body>) -> Result<Response<Body>, Error>;
It would be reasonable to suggest that if the first one is flexible enough to be stored in a container without any fuss, then the second one should as well. As a user of the language, especially in the beginning, I do not want to know of and be penalized by all the crazy transformations that the compiler is doing behind the scene.

And for the record, you can have memory leaks in Rust too. But that's besides the point.

gsserge | 5 years ago | on: Why asynchronous Rust doesn't work

I like to joke that the best way to encounter the ugliest parts of Rust is to implement an HTTP router. Hours and days of boxing and pinning, Futures transformations, no async fn in traits, closures not being real first class citizens, T: Send + Sync + 'static, etc.

I call this The Dispatch Tax. Because any time you want more flexibility than the preferred static dispatch via generics can give you - oh, so you just want to store all these async fn(HttpRequest) -> Result<HttpResponse>, right? - you immediately start feeling the inconvenience and bulkiness of dynamic dispatch. It's like Rust is punishing you for using it. And with async/await taking this to a new level altogether, because you are immediately forced to understand how async funcs are transformed into ones that return Futures, how Futures are transformed into anon state machine structs; how closures are also transformed to anon structs. It's like there's no type system anymore, only structs.

That's one of the reasons, I think, why Go has won the Control Plane. Sure, projects like K8s, Docker, the whole HashiCorp suite are old news. But it's interesting and telling that even solid Rust shops like PingCAP are using Go for their control plane. It seems to me that there's some fundamental connection between flexibility of convenient dynamic dispatch and control plane tasks. And of course having the default runtime and building blocks like net and http in the standard library is a huge win.

That said, after almost three months of daily Rust it does get better. To the point when you can actually feel that some intuition and genuine understanding is there, and you can finally work on your problems instead of fighting with the language. I just wish that the initial learning curve wasn't so high.

gsserge | 5 years ago | on: Rust Cookbook

One thing that is useful to keep in mind when considering boxed errors or error-like objects, e.g. anyhow::Error, is that they famously do not implement std::error::Error trait [1].

This could be a problem if you want to use a third-party API where generic functions or structs use std::error::Error in their trait bounds. For example, fn use_error<E: std::error::Error>(e: E) { ... } will not compile with a boxed error as the parameter.

[1] https://users.rust-lang.org/t/shouldnt-box-t-where-t-error-i...

page 1