I'm new to Dart. The documentation say: "To test whether two objects x and y represent the same thing, use the == operator. (In the rare case where you need to know whether two objects are the exact same object, use the identical() function instead.)"
So, if type this code:
var foo = 'bar';
var baz = 'bar';
print(identical(foo, baz));
If i've well understood, foo and bar do not reference the same object. So identical() must return false, isn't it ?
But it's not the case, at least in DartPad.
Where is the problem.
In this case foo and bar do reference the same object.
That is because the compiler canonicalizes string literals.
The specification requires most constants to be canonicalized. If you create const Duration(seconds: 1) in two places, it will become the same object. Integers, doubles and booleans are always canonicalized, whether constant or not (the language pretends that there is only one instance per value).
Strings are special in that the specification is not entirely clear on whether they need to be canonicalized or not, but constant strings need to be canonicalized for constants to make sense, and all the compilers do that.
A literal is a constant expression, so string literals are always canonicalized. That means that 'bar' denotes the same object no matter where it occurs in your code.
For several built-in "literals", you will always get identical true for equal values.
bool
String
int
double (I think)
Related
Consider code below:
mixin M {}
abstract class I {}
class A = I with M;
class B = I with M;
void main() {
final A? a = A();
final B? b = B();
final I? i1 = a ?? b; // compilation error saying "A value of type 'Object?' can't be assigned to a variable of type 'I?'"
}
Could you please help me to understand why I am having compilation error using "??" operator.
Dart 2.19.0
The a ?? b expression needs to figure out a single type which represents the two possible resulting values, one of type A or one of type B?.
Rather than checking that both A and B? are assignable to I?, it tries to figure out the type of the result of a ?? b as a single type combining both A and B?, which means finding a supertype of both A and B.
To do that, it computes an upper bound using the language-specified "least upper bound" algorithm. (I quote the name, because it finds an upper bound, which is sometimes a least upper bound, but not always.)
The two types are:
A with immediate supertypes I and M, both of which have immediate supertype Object, which has supertype Object? (and all other top types).
B? with supertypes I?, M? and their supertype Object?.
The problem here is that while it can see that I and I? occur in those types, and it can therefore decide that I? is an upper bound,
it also finds M and M? and decides that M? is an upper bound,
and those two types are otherwise completely equivalent, they have the same length of the super-class chain up to Object and are not related to each other. Neither is a least upper bound. So the algorithm ignores them and look for something that's both shared and does not have another type of equal "depth from Object". And it finds Object?.
Which is not assignable to I.
This is a known shortcoming (among several) of the least upper bound algorithm in Dart, but it's a hard problem to solve optimally, because it's very, very easy for a class to introduce some internal private superclass, and sometimes that type then becomes the least upper bound of two public subclasses, leaving users scratching their head.
There are requests to do better, for example by not ignoring the context type, but changing the type inference has to be done very carefully. It can break existing code which has been fine-tuned to the current behavior.
There are no great workarounds here, but there are functional ones.
You will have to rewrite the code to ensure that the static type of the two branches do not both have the unnecessary M type in them.
Rather than down-casting the result to I? at the end, which requires a runtime type check, I'd up-cast the original values:
final I? i3 = a ?? (b as I?); // ignore: unnecessary_cast
That should be completely free up-casts, but may (will!) cause analyzer warnings that you'll have to ignore.
Compiler doesn't seem to recognise that objects a and b are of classes inherited of I in that case.
So the syntax does the same, but in the first case needs help with casting.
final I? i1 = (a ?? b) as I?;
This does not give an error.
I just wanna know about this functionalities and what's effects if we don't use this on every single projects. Thanks for the explanation before.
Example:
int number = 2;
bool isTrue = true;
double floatNumber = 2.1;
Why we not use like this?
var number = 2;
var isTrue = true;
const floatNumber = 2.1;
There are various related Dart style guide rules
PREFER type annotating public fields and top-level variables if the type isn’t obvious.
CONSIDER type annotating private fields and top-level variables if the type isn’t obvious.
AVOID type annotating initialized local variables.
DO annotate when Dart infers the wrong type.
So for example for local variables the style guide even suggests to omit type annotations. The scope of the type annotations is limited to the function/method so the risk is low that inferred types can cause confusion.
For other situations the style guides suggests to add type annotations to avoid ambiguity.
There was a recent addition to Dart that allows to use integer literals to initialize double variables. This only works if the type annotation is added.
var val = 1;
print(val);
1
double val = 1;
print(val);
1.0
This example isn't too practical, because the feature is mostly used to pass literal double values to function/method/constructor parameters, but it demonstrates that the context is important for the decision when to explicitly specify the type using type annotations and when it is safe to omit.
There are also various linter rules that help to stay consistent.
https://pub.dartlang.org/packages/linter
https://dart-lang.github.io/linter/lints/
Are the following two examples equivalent?
Example 1:
let x = String::new();
let y = &x[..];
Example 2:
let x = String::new();
let y = &*x;
Is one more efficient than the other or are they basically the same?
In the case of String and Vec, they do the same thing. In general, however, they aren't quite equivalent.
First, you have to understand Deref. This trait is implemented in cases where a type is logically "wrapping" some lower-level, simpler value. For example, all of the "smart pointer" types (Box, Rc, Arc) implement Deref to give you access to their contents.
It is also implemented for String and Vec: String "derefs" to the simpler str, Vec<T> derefs to the simpler [T].
Writing *s is just manually invoking Deref::deref to turn s into its "simpler form". It is almost always written &*s, however: although the Deref::deref signature says it returns a borrowed pointer (&Target), the compiler inserts a second automatic deref. This is so that, for example, { let x = Box::new(42i32); *x } results in an i32 rather than a &i32.
So &*s is really just shorthand for Deref::deref(&s).
s[..] is syntactic sugar for s.index(RangeFull), implemented by the Index trait. This means to slice the "whole range" of the thing being indexed; for both String and Vec, this gives you a slice of the entire contents. Again, the result is technically a borrowed pointer, but Rust auto-derefs this one as well, so it's also almost always written &s[..].
So what's the difference? Hold that thought; let's talk about Deref chaining.
To take a specific example, because you can view a String as a str, it would be really helpful to have all the methods available on strs automatically available on Strings as well. Rather than inheritance, Rust does this by Deref chaining.
The way it works is that when you ask for a particular method on a value, Rust first looks at the methods defined for that specific type. Let's say it doesn't find the method you asked for; before giving up, Rust will check for a Deref implementation. If it finds one, it invokes it and then tries again.
This means that when you call s.chars() where s is a String, what's actually happening is that you're calling s.deref().chars(), because String doesn't have a method called chars, but str does (scroll up to see that String only gets this method because it implements Deref<Target=str>).
Getting back to the original question, the difference between &*s and &s[..] is in what happens when s is not just String or Vec<T>. Let's take a few examples:
s: String; &*s: &str, &s[..]: &str.
s: &String: &*s: &String, &s[..]: &str.
s: Box<String>: &*s: &String, &s[..]: &str.
s: Box<Rc<&String>>: &*s: &Rc<&String>, &s[..]: &str.
&*s only ever peels away one layer of indirection. &s[..] peels away all of them. This is because none of Box, Rc, &, etc. implement the Index trait, so Deref chaining causes the call to s.index(RangeFull) to chain through all those intermediate layers.
Which one should you use? Whichever you want. Use &*s (or &**s, or &***s) if you want to control exactly how many layers of indirection you want to strip off. Use &s[..] if you want to strip them all off and just get at the innermost representation of the value.
Or, you can do what I do and use &*s because it reads left-to-right, whereas &s[..] reads left-to-right-to-left-again and that annoys me. :)
Addendum
There's the related concept of Deref coercions.
There's also DerefMut and IndexMut which do all of the above, but for &mut instead of &.
They are completely the same for String and Vec.
The [..] syntax results in a call to Index<RangeFull>::index() and it's not just sugar for [0..collection.len()]. The latter would introduce the cost of bound checking. Gladly this is not the case in Rust so they both are equally fast.
Relevant code:
index of String
deref of String
index of Vec (just returns self which triggers the deref coercion thus executes exactly the same code as just deref)
deref of Vec
Consider below struct:
typedef struct _Index {
NSInteger category;
NSInteger item;
} Index;
If I use this struct as a property:
#property (nonatomic, assign) Index aIndex;
When I access it without any initialization right after a view controller alloc init, LLDB print it as:
(lldb) po vc.aIndex
(category = 0, item = 0)
(lldb) po &_aIndex
0x000000014e2bcf70
I am a little confused, the struct already has valid memory address, even before I want to allocate one. Does Objective-C initialize struct automatically? If it is a NSObject, I have to do alloc init to get a valid object, but for C struct, I get a valid struct even before I tried to initialize it.
Could somebody explains, and is it ok like this, not manually initializing it?
To answer the subquestion, why you cannot assign to a structure component returned from a getter:
(As a motivation this is, because I have read this Q several times.)
A. This has nothing to do with Cbjective-C. It is a behavior stated in the C standard. You can check it for simple C code:
NSMakeSize( 1.0, 2.0 ).width = 3.0; // Error
B. No, it is not an improvement of the compiler. If it would be so, a warning would be the result, not an error. A compiler developer does not have the liberty to decide what an error is. (There are some cases, in which they have the liberty, but this are explicitly mentioned.)
C. The reason for this error is quite easy:
An assignment to the expression
NSMakeSize( 1.0, 2.0 ).width
would be legal, if that expression is a l-value. A . operator's result is an l-value, if the structure is an l-value:
A postfix expression followed by the . operator and an identifier designates a member of a structure or union object. The value is that of the named member,82) and is an lvalue if the first expression is an lvalue.
ISO/IEC 9899:TC3, 6.5.2.3
Therefore it would be assignable, if the expression
NSMakeSize( 1.0, 2.0 )
is an l-value. It is not. The reason is a little bit more complex. To understand that you have to know the links between ., -> and &:
In contrast to ., -> always is an l-value.
A postfix expression followed by the -> operator and an identifier designates a member of a structure or union object. The value is that of the named member of the object to which the first expression points, and is an lvalue. 83)
Therefore - that is what footnote 83 explains – ->, &, and . has a link:
If you can calculate the address of a structure S having a component C with the & operator, the expression (&S)->C is equivalent to S.C. This requires that you can calculate the address of S. But you can never do that with a return value, even it is a simple integer …
int f(void)
{
return 1;
}
f()=5; // Error
… or a pointer …
int *f(void)
{
return NULL;
}
f()=NULL; // Error
You always get the same error: It is not assignable. Because it is a r-value. This is obvious, because it is not clear,
a) whether the way the compiler returns a value, esp. whether he does it in address space.
b) when the time the life time of the returned value is over
Going back to the structure that means that the return value is a r-value. Therefore the result of the . operator on that is a r-value. You are not allowed to assign a value to a r-value.
D. The solution
There is a solution to assign to a "returned structure". One might decide, whether it is good or not. Since -> always is an l-value, you can return a pointer to the structure. Dereferencing this pointer with the -> operator has always an l-value as result, so you can assign a value to it:
// obj.aIndex returns a pointer
obj.aIndex->category = 1;
You do not need #public for that. (What really is a bad idea.)
The semantics of the property are to copy the struct, so it doesn't need to be allocated and initialized like an Objective-C object would. It's given its own space like a primitive type is.
You will need to be careful updating it, as this won't work:
obj.aIndex.category = 1;
Instead you will need to do this:
Index index = obj.aIndex;
index.category = 1;
obj.aIndex = index;
This is because the property getter will return a copy of the struct and not a reference to it (the first snippet is like the second snippet, without the last line that assigns the copy back to the object).
So you might be better off making it a first class object, depending on how it will be used.
Any way to declare a new variable in F# without assigning a value to it?
See Aidan's comment.
If you insist, you can do this:
let mutable x = Unchecked.defaultof<int>
This will assign the absolute zero value (0 for numeric types, null for reference types, struct-zero for value types).
It would be interesting to know why the author needs this in F# (simple example of intended use would suffice).
But I guess one of the common cases when you may use uninitialised variable in C# is when you call a function with out parameter:
TResult Foo<TKey, TResult>(IDictionary<TKey, TResult> dictionary, TKey key)
{
TResult value;
if (dictionary.TryGetValue(key, out value))
{
return value;
}
else
{
throw new ApplicationException("Not found");
}
}
Luckily in F# you can handle this situation using much nicer syntax:
let foo (dict : IDictionary<_,_>) key =
match dict.TryGetValue(key) with
| (true, value) -> value
| (false, _) -> raise <| ApplicationException("Not Found")
You can also use explicit field syntax:
type T =
val mutable x : int
I agree with everyone who has said "don't do it". However, if you are convinced that you are in a case where it really is necessary, you can do this:
let mutable naughty : int option = None
...then later to assign a value.
naughty <- Some(1)
But bear in mind that everyone who has said 'change your approach instead' is probably right. I code in F# full time and I've never had to declare an unassigned 'variable'.
Another point: although you say it wasn't your choice to use F#, I predict you'll soon consider yourself lucky to be using it!
F# variables are by default immutable, so you can't assign a value later. Therefore declaring them without an initial value makes them quite useless, and as such there is no mechanism to do so.
Arguably, a mutable variable declaration could be declared without an initial value and still be useful (it could acquire an initial default like C# variables do), but F#'s syntax does not support this. I would guess this is for consistency and because mutable variable slots are not idiomatic F# so there's little incentive to make special cases to support them.