home bbs files messages ]

Forums before death by AOL, social media and spammers... "We can't have nice things"

   comp.os.vms      DEC's VAX* line of computers & VMS.      264,096 messages   

[   << oldest   |   < older   |   list   |   newer >   |   newest >>   ]

   Message 263,795 of 264,096   
   Dan Cross to arne@vajhoej.dk   
   Re: Unsafe code blocks   
   19 Nov 25 16:02:22   
   
   From: cross@spitfire.i.gajendra.net   
      
   In article <10fig0p$1n41a$2@dont-email.me>,   
   Arne Vajhøj   wrote:   
   >On 11/15/2025 9:16 PM, Dan Cross wrote:   
   >>> Also note the availability of the 'Valid attribute to make sure that what   
   >>> is in the variable after the unsafe conversion is actually a valid value.   
   >>   
   >> Sum types make this trivial:   
   >>   
   >>      impl SomeType {   
   >>          fn try_from(i: i32) -> Option {   
   >>              // if valid, return `Some(whatever`),   
   >>              // else return `None`.   
   >>          }   
   >>      }   
   >(assuming Option in Rust is what it is in other languages)   
   >   
   >Option and Ada Valid are somewhat different.   
   >   
   >Option is a way for a function/method to either return   
   >a value or return the fact that there is no value.   
   >   
   >A much better way to do that than traditional   
   >return null or -1 or whatever to indicate there   
   >is no value.   
   >   
   >Ada valid attribute is a runtime check on the result from   
   >an unsafe conversion to see if it meets the constraints   
   >of the data type.   
      
   I'm afraid this misses the point.   
      
   If a language supports sum types, then general solutions like   
   `Option` or `Result` types can be employed to represent the   
   return type of the conversion operation.  If the conversion is   
   invalid, then one returns `None` (or, perhaps, the `Err` variant   
   of a `Result` if one wants to capture what the actual failure   
   was).  That is, the conversion operation itself subsumes the   
   functionality of the valid attribute, and both the value and   
   whether the conversion was valid are represented in the return   
   type.   
      
   Again, taking Rust just as an example, there is a standard way   
   to represent such conversions; the `TryFrom` trait (and it's   
   dual, `TryInto`).  Consider this silly example problem: I have a   
   type representing colors; perhaps the only colors I care about   
   are Red, Green, and Blue. I can label these with integers, say   
   1, 2 and 3, and I can represent this in Rust using an   
   enumeration.   
      
   Now further suppose that I have a file of 8-bit bytes that   
   contain the (raw, integral) representation of these color   
   values; I'd like to convert these into proper objects of the   
   enumeration type, but of course, not all 8-bit byte values   
   represent valid colors, so the proper tool in this case is   
   `TryFrom`.  Note, however, that converting back, from a color to   
   its 8-bit value, is _always_ valid, so I just use `From` in that   
   case (which also, conveniently, gives me `into`).   
      
   Here's the code:   
      
   ```rust   
   #[derive(Clone, Copy, Debug, Eq, PartialEq)]   
   enum Colors {   
       Red = 1,   
       Green = 2,   
       Blue = 3,   
   }   
      
   impl TryFrom for Colors {   
       type Error = String;   
       fn try_from(raw: u8) -> Result {   
           match raw {   
               1 => Ok(Self::Red),   
               2 => Ok(Self::Green),   
               3 => Ok(Self::Blue),   
               _ => Err(format!("unsupported value '{raw}'")),   
           }   
       }   
   }   
      
   impl From for u8 {   
       fn from(color: Colors) -> u8 {   
           color as u8   
       }   
   }   
      
   #[cfg(test)]   
   mod tests {   
       use super::*;   
      
       #[test]   
       fn try_from_raw() {   
           assert_eq!(Colors::try_from(1), Ok(Colors::Red));   
           assert_eq!(Colors::try_from(2), Ok(Colors::Green));   
           assert_eq!(Colors::try_from(3), Ok(Colors::Blue));   
           assert_eq!(Colors::try_from(0), Err("unsupported value '0'".into()));   
       }   
      
       #[test]   
       fn from_clor() {   
           assert_eq!(u8::from(Colors::Red), 1);   
           assert_eq!(u8::from(Colors::Green), 2);   
           assert_eq!(u8::from(Colors::Blue), 3);   
       }   
   }   
   ```   
      
   Note the unit tests.  Note also, that, as a result of the way   
   that the conversion is structured, as aided by the return type,   
   the conversion operation itself is safe.   
      
   Of course, this example is trivial, but the same principle   
   applies for more comple conversions as well, including unsafe   
   conversions.   
      
   Further, by localizing the unsafety inside of the conversion,   
   and using a return type that can represent failure, one can   
   construct a _safe_ interface to do _unsafe_ conversions.   
   Consider a hypothetical in-place conversion from an array of   
   bytes to a string type that is guaranteed to contain only valid   
   UTF-8 sequences, such as a Rust `&str`.  This is an `unsafe`   
   operation, since it relies on the program to assert that the   
   byte array contents are valid UTF-8.  But, my conversion   
   function can probe the input byte sequence for validity, and   
   return `Err` if an invalid sequence is found, otherwise it can   
   do an (unsafe) conversion and return that in an `Ok`.  This is   
   fine, since the conversion function itself enforces the   
   invariants required by the type.   
      
   In contrast, `Valid` is easy to misuse, primarily by not using   
   it at all, in which case you run the risk of raising a runtime   
   exception, or just having an incorrect program.  With either   
   `Option` or `Result`, you are forced to contend with the error   
   case.  The programmer can't forget to check.   
      
   Finally, `Valid` is very limited in its applicability: it can   
   only be used with scalar types.  At least in Rust, `Option` and   
   `Result` are generic over essentially arbitrary types.   
      
   Put another way, if the language supports something like an   
   `Option` type, then there is no need for a special-case facility   
   like Ada's `Valid` attribute.   
      
   	- Dan C.   
      
   --- SoupGate-Win32 v1.05   
    * Origin: you cannot sedate... all the things you hate (1:229/2)   

[   << oldest   |   < older   |   list   |   newer >   |   newest >>   ]


(c) 1994,  bbs@darkrealms.ca