-
Notifications
You must be signed in to change notification settings - Fork 1k
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
Proposal to remove default methods on service traits #221
Comments
I think this is a good idea 👍 |
:( I was busy opening an issue requesting the inverse of this, when I saw this under similar issues. Is it possible to make this optional? Here's the issue that I was opening, I wasn't aware that earlier versions used to generate the default methods. Feature RequestWhen creating an RPC service for a proto file with a lot of endpoints, it becomes tedious to implement all the trait members (esp if one wants to implement just a few). Would it be useful to instead create default implementations of all generated methods like is done in other languages (like Java)? Something like the below: // currently generated code
#[doc = "Generated trait containing gRPC methods that should be implemented for use with GreeterServer."]
#[async_trait]
pub trait Transit: Send + Sync + 'static {
async fn say_hello(
&self,
request: tonic::Request<super::HelloRequest>,
) -> Result<tonic::Response<super::HelloReply>, tonic::Status>;
} // expected generated code
#[doc = "Generated trait containing gRPC methods that should be implemented for use with GreeterServer."]
#[async_trait]
pub trait Transit: Send + Sync + 'static {
async fn say_hello(
&self,
request: tonic::Request<super::HelloRequest>,
) -> Result<tonic::Response<super::HelloReply>, tonic::Status> {
Err(tonic::Status::unimplemented("method not implemented"))
}
} MotivationWhen implementing a subset of gRPC endpoints in Rust, it could be helpful for the user not to be forced to implement all trait methods (like when implementing an ProposalThe generated trait methods should include a default implementation Alternatives
|
Hi @nevi-me The main motivation behind removing default methods was to force implementations to be explicit, even if that means having a bunch of Unlike the I can see how this is inconvenient for your use-case and maybe even for services with a smaller number of methods. However, I would argue that by explicitly implementing a bunch of methods that return Having said that, I am personally not strongly opposed to bringing default implementations back, but if we do, I believe they should panic, instead of returning |
So the big reason we decided to revert the default change was 1) like what @alce said it doesn't fit the same model as |
I'm fine with the current behaviour, as I now have more context. We could defer the inconvenience of implementing the methods to IDEs, as when IDE support gets better, this could be a right-click problem. The thing that made me think of this was actually when I was adding a method to an unrelated Java service. |
Intellij can generate trait methods for you now if you place the generated code inside /src. |
+1 to permitting this again. This is also the behavior of the Go gRPC IMHO, unimplemented-by-default is consistent with the protocol buffers Without this default, a backward-compatible proto change leads to an Regarding IDE support: I use rust-analyzer in my IDE, and rust-analyzer (* edit: I was wrong about this point and have gotten |
I agree that unimplemented by default would be nice to have and is consistent with protobufs. That said, I do not see an advantage with the downsides stated above. If you have any streaming requests this unimplemented by default because very confusing with error messages. I would rather people add the impls and then just use RA should be able to auto generate all the trait fn, if it can't do it now it will in the future as it gets better. This is a supported use case.
A default impl changing is still a breaking change? I don't see how unimplemented by default helps here? That said, users implementing these protobufs are the ones consuming the breaking change so the effect is the least strongest.
I also personally think this is kinda a null point since we are not go/python. I am not sure if java does this or not? I'd rather keep this crate more aligned with rust than anything else and default fn here imo are not idiomatic. They would be idiomatic if they were helper fn but they are apart of the core functionality. I spent a lot of time thinking about how to make the default work but to me it just doesn't fit well and the fact that we don't have default associated types makes this worse. I think we can reconsider once those have been implemented. |
The Go implementation has this behavior to guard against a nil pointer dereference. If you add a method to your service definition but do not provide a value for the corresponding field in the service struct, the compiler won't catch this and the server will crash when trying to call the function. To prevent this, the generated code checks that the handler value is not nil and that it satisfies the service's interface. If the value is nil, it returns We don't need this behavior in Tonic because can have these two checks at compile time. In terms of service and schema evolution, if a client does not know about a method is ok. If a client does not know about a couple of fields is also ok. But a server that just returns Unimplemented is not. It is 'almost' the same behavior as removing a method from the server's implementation, which is a breaking change. |
The client, is not affected because there is no trait to have a impl default on anyways. This only talks about the server side iirc. |
@LucioFranco Agree. But what I'm trying to say is that if a client knows about a method it will call it and having a server that by default returns |
Thanks for your consideration! I think our philosophical disagreement is that, from my perspective, But I’ll defer to your judgment as maintainers here, so let me just say |
I hope my early morning response wasn't too harsh 👍 Yeah, my big issue is that with the fact that streams still require you to implement something really removes the benefit by a large enough amount that it makes me not want to do it. Maybe, in the future we can support it once rust gets the features we need. |
See #1347 for the inverse of this. |
Crates
build
Motivation
When a service trait is generated, tonic-build generates default methods that return
Status::unimplemented
. Although this is nice because the server code compiles with an empty trait implementation, we loose the usefulness of the compiler informing us what to do next.Additionally, the generated methods are not really useful. I don't think many of us would intentionally keep them but can -and most likely will- forget to implement them.
Similarly, when adding new methods and recompiling protos, we loose the nice workflow of relying on the compiler to discover next steps.
As a bonus, some editors have the functionality to generate trait implementations, which we also loose if we have default methods.
Proposal
Update tonic-build to generate service trait definitions only.
The text was updated successfully, but these errors were encountered: