To expand on that: Rust references are guaranteed to be non-null. This allows the compiler to optimize Option<&T> to be the same size as a pointer by encoding None as 0 and Some(p) as the pointer value. When unsafe code violates that guarantee by transmuting 0 to a reference, Some(p) becomes None! Example here: http://is.gd/SIogwi
So 'kam' had a reply to this which is now dead, and I don't
understand why because it looked informative to me. Could someone explain what was wrong with it?:
"To expand on that: Rust references are guaranteed to be non-null. This allows the compiler to optimize Option<&T> to be the same size as a pointer by encoding None as 0 and Some(p) as the pointer value. When unsafe code violates that guarantee by transmuting 0 to a reference, Some(p) becomes None! Example here: h t t p : / / i s . g d / S I o g w i"
(link expanded in case that was the issue).
Sure, although the question asked was "Could someone explain what was wrong with it?", which implies that ajb believed kam's response was killed because it was incorrect.
1) make a URL containing the data to compress (perhaps base-38 encoded or something)
2) Send it to a URL shortening service
3) store the returned link
4) use it every so often to ensure the link doesn't go dead
5) profit!
Not only that, but HN parses the full link incorrectly (the last } isn't included in the link) such that the only way for the link to have worked is to use some indirection.
If this is done in an automated way, it could be an attack vector. EG. a malicious domain or link shortener owner could identify HN server IPs (or via other fingerprints, I'm not really a networks expert), learn when the HN server accesses it, and serve a different URL in only that case to replace the one in the comment.
I know AOL used a similar tactic when Microsoft was racing to keep the (competing) MSN client compatible with their chat protocol after each update[0]. They wouldn't block the MSN client at known Microsoft IP addresses, so the engineers thought their solution worked, while users couldn't access AOL.
Human replacement would probably work fine, and I personally think it would be a good way to deal with behavior that is often an honest mistake made out of ignorance. You might wish to surface the fact the the URL was changed subtly, however, with a color change or diacritical mark.
I actually like that C++ enforces a struct to be at least a byte long because it means you can have pointers to them and they will compare as non equal even if you do not know the type. That is actually quite handy sometimes.
For the same reason any generic type is useful: you can use one implementation for multiple purposes (as a hash map implementation doubles as a perfectly good hash set implementation) without having to duplicate code.
I think that's backwards: a hash set implementation can be used as a hash map simply by storing objects whose comparison is limited to their key fields. A hash map with values that are all the same and never used just seems like a waste.
I can have structs/classes that are zero sized, sometimes this is used for interfaces, or for "namespacing" as alternative to normal namespaces, or for functors, etc.
You may get a NULL pointer or a non-NULL pointer which may not be dereferenced.
This is intentional so that you can always call free on a pointer received from malloc functions, regardless of the allocation result.( passing a NULL to free is ok )
If the size of the space requested is zero, the behavior is implementation-
defined: either a null pointer is returned, or the behavior is as if the size were some
nonzero value, except that the returned pointer shall not be used to access an object.
But, quoting from my ancient copy of The C++ Programming Language, volume 2, §r5.3.3:
"This implies that an operator new() can be called with the argument zero. In this case, a pointer to an object is returned. Repeated such calls return pointers to distinct objects."
In particular, you can't access (read or write) off either end of an allocated block. Well, if the block is of (nominal) length 0, there is nowhere to access safely.
As someone who is pretty good at allocators, if you're calling `malloc(0)` you're wandering into very undefined territory. I've seen everything from valid allocations of non-zero length, whatever was on the stack, and just 0. The only real constraint is that free accepts the returned pointer.
Because Rust doesn't let you compare references. Rather, comparing references actually compares the referred-to value (which requires that said value implements the comparison operators).
What you can do is convert a reference into a raw C pointer `const T`, and then you can compare those addresses. But the reference type itself doesn't make any guarantees about the address, which means you can't make any assumption about the address of the resulting `const T`. Reference types only make guarantees about the semantics of the reference, and since you can't compare the pointer value of references, the pointer value is not something the reference type guarantees.
That still has lots of noise, arguably. Angular brackets somehow feel more noisy to me than the other brackets/braces, they work better as arrows and inequality operators. A single colon would also look better, I think, and the underscore could be dropped...
: doesn't work because that's for type ascription (i.e. specifying what the type of something is).
The angle brackets were chosen because they match the way nearly all C-family languages except D notate generics: C++, Java, C#, Swift, Dart, TypeScript, ActionScript, …
The underscore improves readability here, IMO. We use underscores where they improve readability, similarly to PEP 8.
> As you might imagine, this operation is HIGHLY DANGEROUS.There’s nothing to stop you from transmuting 0 into a borrow, &T and then trying to dereference it. Voila, null pointer dereferencing in Rust!
Is this right? Why doesn't this function need to be marked unsafe?
It's interesting that this is one of the things that Go and Rust have agreed upon: that the addresses of zero-sized values are the same (in Go we spell a zero-sized type as "struct{}").
It has some unexpected surprises though. If you write `var key struct{}` in a package and then try to use &key as a map key (c.f. http://blog.golang.org/context) then you'll collide with other packages. You've got to use a non-zero type instead.
I don't think we actually defined the addresses of zero-sized values to be equal; I believe it's actually unspecified. I don't think this has ever come up in practice, though; we could define them to be equal if we wanted to.
That issue hasn't come up in Rust, probably because equality and hashing look through references (per the definitions of Hash and Eq in the standard library), so you'd really have to go out of your way to try to use unit as a way to make package-specific keys like that.
Cool. Somewhat different but similar... In Julia, types with no fields are automatically singletons and an array size-zero immutable values takes no storage:
This seems like a cute, useless trick but sometimes it's quite useful. For example, the Set{T} is implemented by wrapping a Dict{T,Nothing} and the array of values in the Dict object takes no storage and of course all access is optimized away.
49 comments
[ 3.9 ms ] story [ 109 ms ] threadBecause it needs to compare nonequal to the null reference.
"To expand on that: Rust references are guaranteed to be non-null. This allows the compiler to optimize Option<&T> to be the same size as a pointer by encoding None as 0 and Some(p) as the pointer value. When unsafe code violates that guarantee by transmuting 0 to a reference, Some(p) becomes None! Example here: h t t p : / / i s . g d / S I o g w i" (link expanded in case that was the issue).
Did you have any questions about the explanation, or just whether it was correct?
I know reddit filters them, apparently HN does too.
http://play.rust-lang.org/?code=fn%20main%28%29%20{%0A%20%20...}
HN displays that kinda okay, but if you don't have it looking like HN does, it can get to be almost two thousand characters.
Worst compression algorithm ever:
1) make a URL containing the data to compress (perhaps base-38 encoded or something) 2) Send it to a URL shortening service 3) store the returned link 4) use it every so often to ensure the link doesn't go dead 5) profit!
Like you know, a url shortener.
http://play.rust-lang.org/?code=fn%20main%28%29%20{%0A%20%20...
Would that have been bad in this case?
I know AOL used a similar tactic when Microsoft was racing to keep the (competing) MSN client compatible with their chat protocol after each update[0]. They wouldn't block the MSN client at known Microsoft IP addresses, so the engineers thought their solution worked, while users couldn't access AOL.
Human replacement would probably work fine, and I personally think it would be a good way to deal with behavior that is often an honest mistake made out of ignorance. You might wish to surface the fact the the URL was changed subtly, however, with a color change or diacritical mark.
[0] https://nplusonemag.com/issue-19/essays/chat-wars/
I wonder why Rust does not do that.
Having first-class "void" is very useful. For example, you can make efficient hash sets out of hash maps by simply using () as the value.
For whatever that's worth...
You may get a NULL pointer or a non-NULL pointer which may not be dereferenced.
This is intentional so that you can always call free on a pointer received from malloc functions, regardless of the allocation result.( passing a NULL to free is ok )
But, quoting from my ancient copy of The C++ Programming Language, volume 2, §r5.3.3:
"This implies that an operator new() can be called with the argument zero. In this case, a pointer to an object is returned. Repeated such calls return pointers to distinct objects."
So my memory was from C++ new, not C malloc.
I love the C standards. Is that defining the behavior of malloc or the behavior of the code calling malloc?
What you can do is convert a reference into a raw C pointer `const T`, and then you can compare those addresses. But the reference type itself doesn't make any guarantees about the address, which means you can't make any assumption about the address of the resulting `const T`. Reference types only make guarantees about the semantics of the reference, and since you can't compare the pointer value of references, the pointer value is not something the reference type guarantees.
The angle brackets were chosen because they match the way nearly all C-family languages except D notate generics: C++, Java, C#, Swift, Dart, TypeScript, ActionScript, …
The underscore improves readability here, IMO. We use underscores where they improve readability, similarly to PEP 8.
The author closed it after finding that there were no non-subjective reasons. Moving to []s causes other ambiguities.
Is this right? Why doesn't this function need to be marked unsafe?
http://static.rust-lang.org/doc/master/std/mem/fn.transmute....
It has some unexpected surprises though. If you write `var key struct{}` in a package and then try to use &key as a map key (c.f. http://blog.golang.org/context) then you'll collide with other packages. You've got to use a non-zero type instead.
That issue hasn't come up in Rust, probably because equality and hashing look through references (per the definitions of Hash and Eq in the standard library), so you'd really have to go out of your way to try to use unit as a way to make package-specific keys like that.