protobuf-swift icon indicating copy to clipboard operation
protobuf-swift copied to clipboard

Why do not default values are set to object fields?

Open justoneplanet opened this issue 7 years ago • 2 comments

Hi, Thanks for the nice plugin!! I found the differences in my generated files.

Version of protoc (protoc --version)

libprotoc 2.6.1

Version of ProtocolBuffers.framework

2.4.1

.proto file to reproduce

syntax = "proto2";

message SessionCommand {
  required string name = 1;
};
message Request {
  optional string name = 1;
}
message Input {
  required SessionCommand command = 1;
  optional Request request = 2;
};

Description

In (official) Java, request field is set the default value. It is not null.

  public static final class Request extends
      com.google.protobuf.GeneratedMessage implements
      // @@protoc_insertion_point(message_implements:Request)
      RequestOrBuilder {
    private static final Request defaultInstance;
    public static Request getDefaultInstance() {
      return defaultInstance;
    }
    private void initFields() {
      name_ = "";
    }
    static {
      defaultInstance = new Request(true);
      defaultInstance.initFields();
    }
  }
  public static final class Input extends
      com.google.protobuf.GeneratedMessage implements
      // @@protoc_insertion_point(message_implements:Input)
      InputOrBuilder {
    private Commands.SessionCommand command_ = Commands.SessionCommand.getDefaultInstance();
    private Commands.Request request_ = Commands.Request.getDefaultInstance();
    private void initFields() {
      command_ = Commands.SessionCommand.getDefaultInstance();
      request_ = Commands.Request.getDefaultInstance();
    }
    static {
      defaultInstance = new Input(true);
      defaultInstance.initFields();
    }
  }

On the other hand, it is not set in protobuf-swift.

final public class Input : GeneratedMessage, GeneratedMessageProtocol {
  public private(set) var command:SessionCommand!
  public private(set) var hasCommand:Bool = false
  public private(set) var request:Request!
  public private(set) var hasRequest:Bool = false

In this case, optional string field is set empty string value.

final public class Request : GeneratedMessage, GeneratedMessageProtocol {
  public private(set) var name:String = ""

This is incomprehensible for me. This is a spec or bug?

justoneplanet avatar Aug 13 '16 06:08 justoneplanet

It is designed to be this way.

According to: https://github.com/apple/swift-protobuf/blob/master/Documentation/GENERATED_CODE.md

The original implementation of Swift Protobuf generated properties with Swift optional type, but that obvious approach proved problematic. In practice, testing whether a field is set is fairly uncommon and using Swift optionals directly tended to lead to abuse of the Swift ! operator in many cases where the user knew that certain fields would always be set. Instead we generate fields that use Swift optionals internally (to track whether the field was set on the message) but expose a non-optional value for the field and a separate hasXyz property that can be used to test whether the field was set:

alanscarpa avatar Nov 15 '16 16:11 alanscarpa

That is a silly reason to not use Swift optionals. Most of my protobuf messages contain optionals, and I'm now forced to write code that is more unsafe like so:

// The user now needs to know which messages to make this check for, since only some messages are optional.
if message.hasSubMessage && message.subMessage.hasValue {
    return message.subMessage.value
}

Versus using optional chaining:

return message.subMessage?.value ?? false

The point of optionals is to force the user to handle the potential nil case. If they choose to ignore it with the ! operator, that shouldn't be a concern of this library to fix.

thalmicMark avatar Dec 09 '16 18:12 thalmicMark