Side note. You can always use older versions of crate. So if you just want to follow tutorial, you can pin version of volatile in Cargo.toml to be 0.2. And you can always use API from standard library to do volatile reads and volatile writes.
if you are talking about the the *mut ScreenChar or *const ScreenChar specifically, since the link points to these comments, then the answer is:
*const is enough for this code snippet, since the it calls the unsafe function core::slice::from_raw_parts(), which create a slice from a pair of raw pointer and size:
To nitpick: In Rust parlance, it's not just unsafe (which means may be valid, or "sound", but the compiler can't check it so you need to use unsafe and follow the rules yourself), but specifically unsound, as in it does not follow the rules and the compiler may generate essentially arbitrary behavior without it being a bug:
The previous reference-based design was unsound because it allowed the compiler to insert spurious reads.
You are correct that you should definitely not continue to use unsound APIs!
I would suggest, your code technically does not meet all the Safety requirements of VolatileRef:
Safety
The pointer must be properly aligned.
It must be “dereferenceable” in the sense defined in the core::ptr documentation.
The pointer must point to an initialized instance of T.
You must enforce Rust’s aliasing rules, since the returned lifetime ’a is arbitrarily chosen and does not necessarily reflect the actual lifetime of the data. In particular, while this VolatileRef exists, the memory the pointer points to must not get accessed (read or written) through any other pointer.
The first and second points seem valid, as is the last, but not the third since the VGA memory may not be "initialized" in the abstract machine sense. This might seem arbitrary, since you're probably only going to write to it, but the point of that clause is generally that the compiler is free to insert reads (eg it might read a larger array of bytes than you requested, update some, then write the updated values). Since this is supposed to be volatile I have no idea what that would actually mean, since you're not supposed to get inserted operations, hence the "technically" up top, but that clause is presumably there for a reason, so I would personally feel more comfortable with either VolatilePtr, which has much less stringent requirements, or just using the ptr methods, which only require the safety preconditions at the time of the call and for the range provided.
Has there been a spur in activity recently? Every time I've checked in on the blog in the past few years there was seemingly no activity on github besides community translations.
With FFI the compiler has to pessimistically assume that the FFI happens in a way that doesn't cause UB if there is such a way. As such for exposed memory like the VGA memory, unless you yourself actually write uninitialized data to it, the compiler has to assume that it is initialized if that avoids UB.
That sounds sensible! I wasn't sure how volatile interacts with the AM, most of my experience has been with people explaining it's not fit as a way to write threading and not explaining what it actually did other than a sentence that vaguely says "hardware stuff"
Is there a reason you call this FFI though? I expect, well, a foreign function call, and ptr:: volatile_* (which this crate forwards to) don't seem to count to me, since it just wraps an intrinsic.
I would define FFI as any communication with things outside the abstract machine. That includes both function calls as well as using volatile_* to access foreign memory and foreign code writing to abstract machine owned memory. The way I understand the rules, in all cases the foreign code/hardware has to with respect to the abstract machine behave in a way that is explainable by writing some UB free code that runs on the abstract machine (so it can't just overwrite random parts of the stack), but as the compiler doesn't know what the exact abstract machine code corresponding to the ffi is, it has to pessimistically assume that it could be anything UB free. Things get a bit more complicated with cross-language LTO though. cc Specification of FFI · Issue #421 · rust-lang/unsafe-code-guidelines · GitHub and Specification of inline assembly · Issue #422 · rust-lang/unsafe-code-guidelines · GitHub
That makes sense, IO memory access definitionally has side effects so it needs to be treated as equivalent to an FFI. In effect, volatile_read() having a initialization requirement means you must provide it either memory that's been initialized inside the AM or provide it IO memory that is well behaved to read according to FFI rules (on top of whatever validity requirements T has).
Regardless, I suspect this crate's safety section is being a little overly stringent here, just merging the volatile call safety sections. volatile_write() has no safety clause about the memory needing to be initialized kind you would expect, so it seems like VolatileRef should be able to provide weaker requirements by distributing some or all of those clauses to the read and write APIs.
Given it's seemingly intended as a nicer API on top of the std volatile APIs I doubt they're going to want take advantage of being able to insert additional reads.