Skip to content

Conversation

@k-wasniowski
Copy link

No description provided.

T MoveValue() { return std::move(*value_); }

private:
SFrameError error_;
Copy link
Contributor

Choose a reason for hiding this comment

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

Following the example of Rust's Result<T, E>, it would be slightly tempting to be generic over the error type. OTOH, Rust crates almost always specialize to Result<T> anyway, so we could just skip the intermediate step.

Copy link
Contributor

Choose a reason for hiding this comment

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

This should also be std::optional and we should remove the none variant, though see the comments about variant below.

Copy link
Author

Choose a reason for hiding this comment

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

I think we need to keep none variant due to that an caller could call MoveError/error even though it's success. In that case we need to construct an empty error which reason should rather preferably be none

Copy link

@bbaldino bbaldino Oct 15, 2025

Choose a reason for hiding this comment

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

Is the goal here to emulate rust's Result? Or to emulate RTCError from webrtc? I thought kacper had said the latter, which explains these choices. I don't have a strong opinion one way or another, but would say that we should definitely choose one and be consistent with that.

EDIT: I actually intended this to go under the discussion for ok vs is_ok, but I guess it's a question in general.


private:
SFrameError error_;
std::optional<T> value_;
Copy link
Contributor

Choose a reason for hiding this comment

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

It would be more correct to use std::variant<T, SFrameError> here, since it would force you to have exactly one of value/error populated, not neither or both. The syntax for accessing variants is hideous (std::visit, etc.), but we could isolate inside of this class's methods so that it didn't bother calling code.


bool ok() const { return error_.ok(); }

const T& value() const { return *value_; }
Copy link
Contributor

Choose a reason for hiding this comment

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

Strongly prefer std::get over operator* because it throws if the optional is not present. That won't be any better if built without exceptions, but in other environments, it fails safer.

Copy link
Contributor

Choose a reason for hiding this comment

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

Slightly inclined to mimic the Rust syntax here, with unwrap to request the value and unwrwap_err to request the error.

Copy link
Author

Choose a reason for hiding this comment

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

Switched all places to std::get

Comment on lines 46 to 47
template<typename U>
friend class Result;
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Consider moving to the bottom.

Do we actually need this?

Copy link
Author

Choose a reason for hiding this comment

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

Good catch it's not, leftover from the original on which i was basing. Removed


const T& value() const { return *value_; }

T& value() { return *value_; }
Copy link
Contributor

Choose a reason for hiding this comment

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

Also add T&& value() &&?

Copy link
Author

Choose a reason for hiding this comment

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

Not sure if we need this access operator at all. Since i would rather use MoveValue() in all places. Maybe it's better to remove MoveValue, and make only T&& value() && ??

return std::move(error_);
}

bool ok() const { return error_.ok(); }
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Mimicking Rust would have this as is_ok(), and possibly a parallel is_err().

Copy link
Author

Choose a reason for hiding this comment

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

Replaced existing ok() with is_ok, and added is_err, not sure though if we need the latter

// Helper functions to create Results
template<typename T>
Result<T>
Ok(const T& value)
Copy link
Contributor

Choose a reason for hiding this comment

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

I like where you're going with this, but to match C++ style, these should probably be ok and err. You could also make them static functions on Result<T>, which would give you Result<T>::ok(value), but unfortunately, you would have to include the <T> because of the limits of C++ type inference.

Copy link
Contributor

Choose a reason for hiding this comment

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

... though now I see that you need to do that for Err anyway, so yeah, I would probably go with Result<T>::err().

Copy link
Author

Choose a reason for hiding this comment

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

Done

src/header.cpp Outdated
Comment on lines 92 to 93
// Handle error - for now, throw or return default
throw std::runtime_error("Failed to decode uint");
Copy link
Contributor

Choose a reason for hiding this comment

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

You might want to define a macro for this, something like:

#defin CHECK(rv) if (!(rv).ok()) { return rv; }

See also: https://searchfox.org/firefox-main/search?q=%23define+ENSURE&path=&case=true&regexp=false


private:
SFrameErrorType type_ = SFrameErrorType::none;
std::string message_;

Choose a reason for hiding this comment

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

Part of the "which model should we emulate" discussion, but it appears that the message is optional so I wonder if this should be optional?

T MoveValue() { return std::move(std::get<T>(data_)); }

private:
std::variant<SFrameError, T> data_;

Choose a reason for hiding this comment

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

An example of being consistent: if we are going to emulate rust's result, I think we should model the type first here.

if (!is_length) {
// Nothing to read; value is already in config byte
return { value_or_length, data };
return Result<std::tuple<uint64_t, input_bytes>>::ok(

Choose a reason for hiding this comment

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

Can we omit the explicit template types in these return values since they're specified in the signature?

const auto value = decode_uint(data.subspan(0, size));
auto value_result = decode_uint(data.subspan(0, size));
if (!value_result.is_ok()) {
return value_result.MoveError();

Choose a reason for hiding this comment

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

I don't think I'm following the need for the MoveError method/call here (probably just my c++ skills are too eroded). Why can't we just return value_result here?

Copy link
Author

Choose a reason for hiding this comment

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

value_result have type sframe::Result<uint64_t>
this function expects to return Result<std::tuple<uint64_t, input_bytes>> so it doesnt match.

I return here with MoveError(), to trigger implicit error creation from move constructor.

To be able to return value_result i would need to define conversion between those two return types.

Choose a reason for hiding this comment

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

Ah ha, ok, didn't notice there was a conversion happening there.

public:
template<class... Args>
void emplace(Args&&... args)
Result<void> emplace(Args&&... args)
Copy link
Author

Choose a reason for hiding this comment

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

This changes the signature of the method. Probably will need to define new method to perform injecton

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.

3 participants