-
Notifications
You must be signed in to change notification settings - Fork 71
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
Confusing safety contract of is_u8 #24
Comments
What do you mean it's safe to implement? Does Rust allows to implement a trait method that is marked Also if you have suggestion how to the handle it better, I'd prefer to do this. The issue is we want to implement Borsh serialization and deserialization of |
BorshSerialize and BorshDeserialize are safe traits -- by language semantics that means it is unsound for the memory safety / thread safety of the program to hinge on either of them being implemented "correctly". This is 100% orthogonal to whether an individual trait method is safe or unsafe to call (which is what unsafe fn means). A trait that is unsafe to implement can contain methods that are safe to call, and a trait that is safe to implement can contain methods that are unsafe to call. Reference: https://doc.rust-lang.org/1.50.0/reference/items/traits.html#unsafe-traits |
Yeah, that's all true. I believe there's no clean way to express this in Rust (what we ideally want is an unsafe super-trait, but that requires specialization, bringing us back to the square 1). #25 implements a simpler solution of just hiding the method from crate's public API. |
|
One pattern that can be used in order to have a safe trait rely on guarantees is the following: struct Guarantee { _private: () }
impl Guarantee {
// Safety: only call if ...
pub unsafe fn promise() -> Self { Guarantee { _private: () } }
}
trait RequiresGuarantee {
fn check_guarantee(&self) -> Guarantee;
} Then users can call |
To be clear, the underlying issue was solved way nicer by dtolnay here by just not using @RalfJung there's a twist here, in that we want to (unsafely) override this for u8, and, for all other types, use default (safe) impl. Or, as I've said, we could use @cramertj that won't work exactly as spelled -- the user user can get // You write code like
struct CramertjType;
impl RequiresGuarantee for CramertjType {
fn check_guarantee(&self) -> Guarantee {
// Carefully verify invariants
unsafe { Guarantee::promise() }
}
}
// I write code like
struct MatkladType;
impl RequiresGuarantee for MatkladType {
fn check_guarantee(&self) -> Guarantee {
// Life's too short for verifying invariants, lets use someone else's promise
CramertjType::check_guarantee()
}
} |
You're right, good point!
…On Wed, Apr 21, 2021, 7:50 AM Aleksey Kladov ***@***.***> wrote:
To be clear, the underlying issue was solved way nicer by dtolnay here by
just not using is_u8 at all.
@RalfJung <https://github.com/RalfJung> there's a twist here, in that we
want to (unsafely) override this for u8, and, for all other types, use
default (safe) impl. Or, as I've said, we *could* use unsafe trait, but
that needs specialization.
@cramertj <https://github.com/cramertj> that won't work exactly as
spelled -- the user user can get Guarantee without unsafe by calling
RequiresGuarantee::check_guarantee on some *other* type that implements
it. You need to make check_guarantee unsafe to call as well:
// You write code likestruct CramertjType;
impl RequiresGuarantee for CramertjType {
fn check_guarantee(&self) -> Guarantee {
// Carefully verify invariants
unsafe { Guarantee::promise() }
}
}
// I write code likestruct MatkladType;
impl RequiresGuarantee for MatkladType {
fn check_guarantee(&self) -> Guarantee {
// Life's too short for verifying invariants, lets use someone else's promise
CramertjType::check_guarantee()
}
}
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#24 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABNP2KPFXKTZC7T7VAK7F2TTJ3Q3XANCNFSM4ZNIANIQ>
.
|
You could use |
BorshSerialize and BorshDeserialize have an
is_u8
trait method which is unsafe to call (but safe to implement). What contract is the caller required to uphold when callingis_u8
in order for the call to be sound?The text was updated successfully, but these errors were encountered: