@frangio (Francisco Giordano) posted a new topic, subject "Implementing unsafe fn in trait with a safe fn". It read:

   Given a trait with an `unsafe fn` member, why is it not possible to write an impl for this trait where the function is _not_ marked unsafe? I see unsafe as a requirement for the caller of the trait function, not as a requirement for the implementer.
    
    Suppose I have a trait with a function that is in general unsafe to call, but a particular implementation of this function is perfectly safe. I would prefer to not mark this function implementation unsafe so as to avoid mistakenly writing unsafe code in it.

@CAD97 (Christopher Durham) replied

    Unfortunately this isn't possible currently; you have to acknowledge the trait signature as written, and that includes `unsafe`.
    
    In general, I don't know if a safe trait can soundly have an `unsafe` method; what does the caller know about calling the method if it can't trust implementations to behave sanely?
    
    The exact semantics of an unsafe method on a safe trait is unclear, so in general I'd recommend avoiding it.
    
    If you want to require `unsafe` blocks within an `unsafe fn`, the current way to do so is to define a safe function which it delegates to. This can be done within the outer `unsafe fn` declaration to closely scope the code.
    [code] 
        unsafe fn example() {
            fn example_() {
                // use of unsafe fn is a compile error here
            }
            example_()
        }
    [/code]

To which @frangio (Francisco Giordano) replied

    CAD97:
    
    > If you want to require `unsafe` blocks within an `unsafe fn` , the current way to do so
    
    This is a great tip, thank you.
    
    CAD97:
    
    > I don't know if a safe trait can soundly have an `unsafe` method; what does the caller know about calling the method if it can't trust implementations to behave sanely?
    
    It's an interesting point. How is it different from a normal unsafe function though? An unsafe function relies on documentation to specify the preconditions to safely using it, and the same would be true for an unsafe method in a trait: all implementors would have to adhere to whatever is documented. Do you think that this extra requirement means the trait should be unsafe as well?

So @CAD97 (Christopher Durham) replied

    The difference is the distance between the docs and the implementation.
    
    With a regular free `unsafe fn`, the declaration and docs are in the same location as the implementation. With trait methods, the declaration and docs are distinct from the many potential implementations.
    
    I think the semantics of a safe trait implementation of an unsafe method would be that the documented preconditions are required to hold, and execution soundness must only at maximum rely on those preconditions, but the caller may not assume anything about the postcondition state.
    
    So for example, a safe trait unsafe function given a pointer parameter guaranteed to be writable could write complete garbage to the pointer, no matter the docs.
    
    But the ambiguity of the interpretation means that it's probably a bad idea in practice. The unsafe trait encodes that the implementation must be correct to the docs to uphold soundness. A safe trait is allowed to do any safe/sound action on its method's inputs, no matter the docs.

Then @Yandros (Daniel H-M) said

    CAD97:
    
    > If you want to require `unsafe` blocks within an `unsafe fn` , the current way to do so
    
    Or easier: use the `#[require_unsafe_in_body]` proc macro attribute. The more users using this crate, the more likely it is that a future RFC (for a future Rust edition) that made `unsafe fn`s still require `unsafe {}` blocks inside it, be accepted.
    
    * * *
    
    CAD97:
    
    > I don't know if a safe trait can soundly have an `unsafe` method; what does the caller know about calling the method if it can't trust implementations to behave sanely?
    
    The way I see it, if a non-`unsafe` trait has an `unsafe` method that can be overriden, then it is `unsafe` to call such method in a generic context.
    [code] 
        trait Trait { unsafe fn unsafe_function (...args) -> Ret; }
        
        unsafe // <---------------------------------------------------+
        fn generic_function<T : Trait> (...args) -> Ret            // |
        {                                                          // |
            // Safety? the safety contract of unsafe_function depends |
            // on T, thus requiring that this whole function          |
            // be marked unsafe --------------------------------------+
            <T as Trait>8b7c:f320:99b9:690f:4595:cd17:293a:c069unsafe_function(...args)
        }
        
    [/code]
    
    Indeed, someone would otherwise be able to do:
    [code] 
        struct Evil;
        impl Trait for Evil {
            unsafe
            fn unsafe_function (...args) -> Ret { 8b7c:f320:99b9:690f:4595:cd17:293a:c069st8b7c:f320:99b9:690f:4595:cd17:293a:c069hint8b7c:f320:99b9:690f:4595:cd17:293a:c069unreachable_unchecked() }
        }
        
    [/code]
    
    and it shouldn't then be possible to call such function from within non-`unsafe` code.
    
    ###  Conclusion
    
    If the method can be overriden, and the trait is not itself `unsafe` (so as to put the burden of correctness on the implementor of the trait), then it must always be `unsafe` to call this function in a generic context.
    
    So the only way to get such an `unsafe` associated function be callable from within a non-`unsafe` `fn` without breaking soundness, all the implementations of the function must be known:
    [code] 
        trait Trait : TraitExt {
            // (non-`unsafe`) items 
        }
        trait TraitExt {
            unsafe
            fn associated_function (...args) -> Ret;
        }
        
        impl<T : ?Sized + Trait> TraitExt for T {
            unsafe
            fn associated_function(...args) -> Ret
            {
                // sole implementation, allowed to use the non-unsafe items
            }
        }
        
    [/code]

So @system (system) replied

    This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.


{'source': 'discourse', 'url': 'https://users.rust-lang.org/t/implementing-unsafe-fn-in-trait-with-a-safe-fn/36289', 'title': 'Implementing unsafe fn in trait with a safe fn', 'views': 452, 'has_accepted_answer': False, 'word_count': 869, 'like_count': 2, 'reply_count': 3, 'participant_count': 3, 'posts': 6, 'tags': '', 'author': '@frangio (Francisco Giordano)', 'participants': '@frangio (Francisco Giordano), @CAD97 (Christopher Durham), @Yandros (Daniel H-M), @system (system)', 'image_url': [None], 'created_at': '2019-12-29T21:53:50.144Z', 'last_posted_at': '2020-03-29T16:24:54.031Z'}