How do I iterate over the fields of a ref object in nim? - reference

I have a ref object type and would like to iterate over all of its fields and echo them out.
Here an example of what I want:
type Creature* = ref object
s1*: string
s2*: Option[string]
n1*: int
n2*: Option[int]
n3*: int64
n4*: Option[int64]
f1*: float
f2*: Option[float]
b1*: bool
b2*: Option[bool]
var x = Creature(s1: "s1", s2: some("s2"), n1: 1, n2: some(1), n3: 2, n4: some(2.int64), f1: 3.0, f2: some(3.0), b1: true, b2: some(true))
for fieldName, fieldValue in x.fieldPairs:
echo fieldName
However, doing so causes this compiler error:
Error: type mismatch: got <Creature>
but expected one of:
iterator fieldPairs[S: tuple | object; T: tuple | object](x: S; y: T): tuple[
key: string, a, b: RootObj]
first type mismatch at position: 1
required type for x: S: tuple or object
but expression 'x' is of type: Creature
iterator fieldPairs[T: tuple | object](x: T): tuple[key: string, val: RootObj]
first type mismatch at position: 1
required type for x: T: tuple or object
but expression 'x' is of type: Creature
expression: fieldPairs(x)
Going through the documentation, there appear to be no iterators for ref object types, only for object types. If that's the case, then how do you iterate over ref object types?

If you want to use iterators, you need to de-reference the ref-type that you want to iterate over! This may also apply to any other proc that expects an object parameter, but that you want to use with a ref object instance.
In nim, the de-referencing operator is [].
So in order to work, the instance x of the ref object type Creature needs to be de-referenced before iterating over it:
for fieldName, fieldValue in x[].fieldPairs:
echo fieldName
This will also work with any proc you write, for example like this:
proc echoIter(val: object) =
for fieldName, fieldValue in val.fieldPairs:
echo fieldName
echoIter(x[])

Related

Narrowing down a type from function return types [duplicate]

This question already has an answer here:
Python typing: Narrowing type from function that returns a Union
(1 answer)
Closed 9 months ago.
I have a function which might return values of two different types, e.g. Union[str, int]. The type of the returned value can be determined from the function argument. So on each call, I know which type should be returned. However, mypy complains that Union[str, int] type cannot be assigned to a stricter type, e.g. str. Is there a way to allow stricter type assignment, without completely turning type checking off (e.g. with # type: ignore comment)?
For example, if I have this function:
from typing import Union
def change_value(action: str, value: int) -> Union[str, int]
if action == "stringify":
return str(value)
return value
stringified_value: str
stringified_value = change_value("stringify", 10)
Mypy complains with this error:
Expression of type "str | int" cannot be assigned to declared type "str"
I would like to remove mypy error by somehow telling mypy, that in this case type narrowing down is always correct.
It can be done using overloads:
from typing import Literal, Union, overload
#overload
def change_value(action: Literal['stringify'], value: int) -> str: ...
#overload
def change_value(action: Literal['whatever_else'], value: int) -> int: ...
def change_value(action: str, value: int) -> Union[str, int]:
if action == "stringify":
return str(value)
return value
stringified_value: str = change_value("stringify", 10)
playground
This approach has an additional benefit: you explicitly tell that stringify and whatever_else are two only possible actions, and call like change_value('stingify', 2) will be denied by type checker (noticed the typo? If not, it would be hard to debug otherwise).

How can I pass subclass to proc expecting argument of parent type?

It's possible to manually convert seq[Child] to seq[Parent] but maybe there's a better option?
type
ParentRef = ref object of RootObj
a: int
ChildRef = ref object of ParentRef
b: int
let parents = #[ParentRef()]
let children = #[ChildRef()]
proc process(list: seq[ParentRef]): void = discard list
process(parents)
process(children) # <== error
Nim has a stronger type system than many, by default it only implicitly converts types according to these rules.
We can see there that a sub-class is convertible to its superclass,
but seq[type1] is only convertible to seq[type2] if type1==type2, i.e. they are identical, not subtypes.
To add another implicit conversion relationship, one defines a type-converter, either case by case:
converter seqChildToSeqParent(c:seq[ChildRef]):seq[ParentRef]= c.mapIt(it.ParentRef)
or generically for any subtype:
converter toSeqParent[T:ParentRef](x:seq[T]):seq[ParentRef]= x.mapIt(it.ParentRef)
With one of those converters defined, the compiler will convert for you automatically, and call to process(children) will compile and run.
Maybe the better option is to use a generic proc?
type
Parent = object of RootObj
a: int
Child = object of Parent
b: int
let parents = #[Parent(a: 2)]
let children = #[Child(a: 3, b: 5)]
proc process[T: Parent](list: seq[T]): void =
echo repr(list)
echo "Accessing generic attribute a: ", list[0].a
process(parents)
process(children)
In fact, if you don't add the T : Parent restriction that proc will work for anything as long as the compiler finds all the fields it wants on the type:
type
Parent = object of RootObj
a: int
Child = object of Parent
b: int
FooBar = object of RootObj
a: int
bar: string
let parents = #[Parent(a: 2)]
let children = #[Child(a: 3, b: 5)]
let foobars = #[FooBar(a: 42, bar: "Yohoo")]
proc process[T](list: seq[T]): void =
echo repr(list)
echo "Accessing generic attribute a: ", list[0].a
process(parents)
process(children)
process(foobars)

How to explicitly annotate the type of an array literal

I have this Rusqlite code:
use rusqlite::types::ToSql;
// ... normal Rusqlite initialisation code ...
let mut statement = tx.prepare("INSERT INTO table VALUES (?1, ?2)")?;
let params: &[&dyn ToSql] = &[
&0u32,
&"hello",
];
statement.execute(params)?;
The ?1 parameter is an INTEGER and the ?2 parameter is TEXT. This compiles, however if I move the params into the function call it does not compile:
statement.execute(&[
&0u32,
&"hello",
])?;
This gives the following error for &hello.
mismatched types
expected type `&u32`
found reference `&&'static str`
It seems like it infers the type for the array literal based on the type of the first element. What is the syntax for explicitly setting the type of the array?
You annotate the type of an array literal the same way as any other type, by writing the type after the variable name, separated by a colon:
let array: [u8; 5] = [1, 2, 3, 4, 5];
That's not your problem. Your problem is that you are creating a heterogeneous array, one where the types of each element differ. The first element is a reference to an integer, the second a string, etc. You need to perform the case to the trait object more eagerly:
let x = [&42 as &dyn Display, &true];
For rusqlite specifically, use the params! macro to do this for you:
use rusqlite::params; // 0.23.1
fn main() {
let x = params![&42, &true];
}
See also:
How to create a vector of boxed closures in Rust?
Use Option::map to Box::new a trait object does not work
How to coerce a Vec of structs to a Vec of trait objects?
Why does Rusqlite reject the Option type when executing a query?
Cannot call rusqlite's query because it expects the type &[&rusqlite::types::ToSql]

User defined type as reference

Are these two definitions equivalent?
type
PersonObj = object
name: string
age: int
PersonRef = ref PersonObj
type
PersonObj = ref object
name: string
age: int
In the latter should PersonObj be simply called Person?
They are not equivalent as the first PersonObj is not ref, while the second is. To be equivalent the second definition should read as
type PersonRef = ref object
name: string
age: int
Now Obj or Ref suffixes is your own decision. Usually the suffixes are not used if the type is intended to be always used either as value type or a ref type, so it would be just:
type Person = ref object
name: string
age int

The difference between t and *t

package main
import "fmt"
type TT struct {
a int
b float32
c string
}
func (t *TT) String() string {
return fmt.Sprintf("%+v", *t)
}
func main() {
tt := &TT{3, 4, "5"}
fmt.Printf(tt.String())
}
The code can work well. But if I change the String method as in the following, it will cause dead loop. The difference is that the *t is replaced with t. Why?
func (t *TT) String() string {
return fmt.Sprintf("%+v", t)
}
Because the fmt package checks if the value being printed has a String() string method (or in other words: if it implements the fmt.Stringer interface), and if so, it will be called to get the string representation of the value.
This is documented in the fmt package doc:
[...] If an operand implements method String() string, that method will be invoked to convert the object to a string, which will then be formatted as required by the verb (if any).
Here:
return fmt.Sprintf("%+v", *t)
You are passing a value *t of type TT to the fmt package. If the TT.String() method has a pointer receiver, then the method set of the type TT does not include the String() method, so the fmt package will not call it (only the method set of *TT includes it).
If you change the receiver to non-pointer type, then the method set of the type TT will include the String() method, so the fmt package will call that, but this is the method we're currently in, so that's an endless "indirect recursion".
Prevention / protection
If for some reason you do need to use the same receiver type as the type of the value you pass to the fmt package, an easy and common way to avoid this / protect from it is to create a new type with the type keyword, and use type conversion on the value being passed:
func (t TT) String() string {
type TT2 TT
return fmt.Sprintf("%+v", TT2(t))
}
Try this on the Go Playground.
But why does this work? Because the type keyword creates a new type, and the new type will have zero methods (it does not "inherit" the methods of the underlying type).
Does this incur some run-time overhead? No. Quoting from Spec: Type declarations:
Specific rules apply to (non-constant) conversions between numeric types or to and from a string type. These conversions may change the representation of x and incur a run-time cost. All other conversions only change the type but not the representation of x.
Read more about this here: Does convertion between alias types in Go create copies?

Resources