Afew begginer questions [Solved]


#1

Hi.
Sorry for asking more than one question in a topic. Laziness strikes again …

  • How to implement a type’s coercion to some other type (or how to overload “as” keyword. I’m not talking about Deref coercions)
  • Is data race even possible in single threaded software? If yes I need examples!
  • Why only the last field of structs may have a dynamically sized type? whats the secret? I’m new in memory design and assembly. But I know some basics.
  • When should I derefrence a reference in order to be able to use it? for example I can’t put a reference in a function expecting variable. and vice versa. but println! accepts both references and variables.
  • How to make to traits incompatible. like Copy and Drop. So when something implements one, it shoudn’t be able to implement the other.
  • I finished the book. whats the next step?

#2
  • You cannot overload “as”. Here’s a list of other things you can do to facilitate type conversions.
  • A data race in the strict sense of the word (i.e. undefined memory behaviour trigger by one thread writing to a memory region and another reading/writing concurrently) cannot happen in a single-threaded program. There are, however, a couple of issues with shared mutable state in a single-threaded program, iterator invalidation being the textbook example.
  • The compiler needs to know how to locate the fields of your structs at compile time in order to generate efficient code that accesses them. If the size of some struct field is defined at run time, then determining the location of the next struct field at compile time is impossible. In principle, one could imagine using the data equivalent of a vtable in order to locate the struct fields at runtime, but in practice that would be as slow as a pointer and no one needed it so far.
  • The basic rule is that Rust dereferences for you when you’re calling a method of the object, and you need to dereference when you want to pass a value to someone else (a struct member, a function…) in order to disambiguate between passing a reference and the associated value. In practice, more advanced code will sometimes muddy the water by accepting AsRef<T> instead of T in order to homogeneously handle owned and borrowed types (e.g. String and &str).
  • I did not understand this question.
  • Depending on your personality, you may want to read more learning material, or find yourself a simple coding project and get some Rust experience at building something. Or both at the same time.

#3

thanks for answer.
In the 5th question by saying two incompatible traits I meant if something implements one, it shouldn’t be able to implement the other. for example Drop and Copy traits. Something like the opposite of supertrait. dependency and incompatibility


#4

Some answers:

You can’t overload as. But you can implement the From trait, for conversions that consume the value, or the AsRef or Borrow traits.

You can’t get a data race per se in single-threaded software. Here’s Java’s definition of a data race:

A data race occurs when:

  • two or more threads in a single process access the same memory location concurrently, and
  • at least one of the accesses is for writing, and
  • the threads are not using any exclusive locks to control their accesses to that memory.

So being multi-threaded is a prerequisite.

What you can get in single-threaded code is reentrancy errors. Rust prevents these. For example:

fn append(vec: &mut Vec<i32>, elts: &[i32]) {
    vec.extend(elts);
}

fn main() {
    let mut v = vec![0,1,0,-1];
    append(&mut v, &v);
    println!("{:?}", v);
}

This doesn’t compile in Rust (playground), but if it did, it could be a very serious bug if v were reallocated to make space for more elements while append's argument elts was still pointing at the old elements. This kind of mistake, where code assumes two pointers are distinct and chokes when they’re actually referring to the same thing, is quite common. I think you can look at it as a very simple case of unexpected reentrancy - where a data structure is being consulted while it’s in the midst of being modified. In Java, this kind of problem often shows up as a ConcurrentModificationException.

When Rust lays out a struct in memory, it places its fields’ values directly in the struct, rather than just storing pointers to them, the way JavaScript, Python, or Java would. Rust would like to be able to assign each field a fixed offset from the start of the struct. That is, if I say:

struct Point { x: f32, y: f32 }

Rust wants to be able to say that, if a Point lies at address A, then its x field always lies at address A + 0, and its y field always lies at A + 4. (Or something like that; Rust doesn’t promise exactly how it will lay out structs.) This means that simply knowing a value’s type and address is all you need to find all its fields, and there’s no need to store hidden fields in the value.

So to your question: If you had dynamically sized values in the midst of a struct, then any subsequent members’ offsets would vary dynamically, which Rust wants to avoid.

Rust’s . operator automatically follows references in its left operand, so you can write r.x instead of (*r).x. It will also borrow a reference to its left operand if necessary, so you can write v.len(), not (&v).len().

Comparison operators also implicitly borrow references to their operands, so you can write string1 == string2, and not lose ownership of either. Or, if you have references, comparison operators automatically follow them. So if you have two &i32 values r1 and r2, you can write r1 < r2, instead of *r1 < *r2.

If you pass println! a reference, it implicitly dereferences it and just prints the value it refers to. It also implicitly borrows references to its arguments, so (say) passing a String to println! doesn’t steal ownership of it from the caller. So println! is designed to be especially flexible, more so than an ordinary function.

There are probably a few other cases that automatically dereference, for convenience. But otherwise, you generally do need to write out uses of the & and * operators. I usually just let the compiler tell me when I’ve gotten it wrong. You’ll get used to it eventually, and your mistakes will become rarer.

(Details: println! uses the formatting traits from the std::fmt module, like Display and Debug, to produce the textual form of a value. If a type T implements a formatting trait, then there is a generic implementation of that trait for &T and &mut T as well that simply follow the reference and format the value it points to. The comparison operators are driven by the PartialEq and PartialOrd traits, which have similar generic implementations.)

I’m pretty sure I don’t understand this question; can you expand?

Programming is like playing violin or piano: it takes thousands of hours of practice before you can say you’re good. The people who get good are those who put in the hours; whether they do so because they love it or because they are disciplined doesn’t matter. So treat Rust like learning a new instrument. Read Rust code written by great programmers (Raph Levien and Tomaka are two of my favorites, but there are lots of others). Contribute to a project you like. Write lots of code yourself.


#5

Very useful information. thanks.
I edited the fifth question


#6

I can’t think of any way to express that two traits are mutually exclusive in Rust.


#7

I think there is not a way to express mutually exclusive traits at present. Although it is an often requested feature.